Modificaciones y se agrego

This commit is contained in:
santiago
2025-09-23 00:05:20 +02:00
parent 031998b006
commit c347a6185a
5 changed files with 337 additions and 0 deletions
+100
View File
@@ -10,9 +10,12 @@
"dependencies": {
"axios": "^1.12.2",
"bootstrap": "^5.3.8",
"file-saver": "^2.0.5",
"moment": "^2.30.1",
"next": "15.5.3",
"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",
@@ -210,6 +213,54 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@floating-ui/core": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz",
"integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==",
"dependencies": {
"@floating-ui/utils": "^0.2.10"
}
},
"node_modules/@floating-ui/dom": {
"version": "1.7.4",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz",
"integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==",
"dependencies": {
"@floating-ui/core": "^1.7.3",
"@floating-ui/utils": "^0.2.10"
}
},
"node_modules/@floating-ui/react": {
"version": "0.27.16",
"resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.16.tgz",
"integrity": "sha512-9O8N4SeG2z++TSM8QA/KTeKFBVCNEz/AGS7gWPJf6KFRzmRWixFRnCnkPHRDwSVZW6QPDO6uT0P2SpWNKCc9/g==",
"dependencies": {
"@floating-ui/react-dom": "^2.1.6",
"@floating-ui/utils": "^0.2.10",
"tabbable": "^6.0.0"
},
"peerDependencies": {
"react": ">=17.0.0",
"react-dom": ">=17.0.0"
}
},
"node_modules/@floating-ui/react-dom": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz",
"integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==",
"dependencies": {
"@floating-ui/dom": "^1.7.4"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@floating-ui/utils": {
"version": "0.2.10",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
"integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="
},
"node_modules/@humanfs/core": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
@@ -2382,6 +2433,14 @@
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
"license": "MIT"
},
"node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
"engines": {
"node": ">=6"
}
},
"node_modules/color": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
@@ -2527,6 +2586,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/date-fns": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/kossnocorp"
}
},
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@@ -3343,6 +3411,11 @@
"node": ">=16.0.0"
}
},
"node_modules/file-saver": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz",
"integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
},
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@@ -4442,6 +4515,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/moment": {
"version": "2.30.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
"engines": {
"node": "*"
}
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -4943,6 +5024,20 @@
}
}
},
"node_modules/react-datepicker": {
"version": "8.7.0",
"resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-8.7.0.tgz",
"integrity": "sha512-r5OJbiLWc3YiVNy69Kau07/aVgVGsFVMA6+nlqCV7vyQ8q0FUOnJ+wAI4CgVxHejG3i5djAEiebrF8/Eip4rIw==",
"dependencies": {
"@floating-ui/react": "^0.27.15",
"clsx": "^2.1.1",
"date-fns": "^4.1.0"
},
"peerDependencies": {
"react": "^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc",
"react-dom": "^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc"
}
},
"node_modules/react-dom": {
"version": "19.1.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
@@ -5647,6 +5742,11 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/tabbable": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz",
"integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="
},
"node_modules/tinyglobby": {
"version": "0.2.15",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+3
View File
@@ -11,9 +11,12 @@
"dependencies": {
"axios": "^1.12.2",
"bootstrap": "^5.3.8",
"file-saver": "^2.0.5",
"moment": "^2.30.1",
"next": "15.5.3",
"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",
+26
View File
@@ -0,0 +1,26 @@
import Cuestionario from "@/components/administrador/cuestionario";
import Reporte from "@/components/administrador/reporte";
import BotonRegresar from "@/components/boton-regresar";
export default function Registro() {
const years = [2020, 2021, 2022, 2023, 2024, 2025];
const admin = { token: "mi_token_de_prueba" };
//const updateIsLoading = (loading: boolean) => {
// console.log("Loading:", loading);
//};
return (
<main>
<BotonRegresar />
<h3 className="title">Reportes</h3>
<Cuestionario
years={years}
admin={admin}
//updateIsLoading={updateIsLoading}
/>
<Reporte admin={admin}/>
</main>
);
}
@@ -0,0 +1,95 @@
"use client"
import { axiosInstance } from "@/api/config";
import { useState } from "react";
import { Button, Col, FormGroup, FormLabel, FormSelect, InputGroup } from "react-bootstrap";
interface Props {
years: number[];
admin: {token: string};
updateIsLoading?: {loading: boolean};
}
export default function Cuestionario({ years, admin, updateIsLoading }: Props) {
const [selectedCuestionario, setSelectedCuestionario] = useState("");
const [version, setVersion] = useState("");
const [selectedYear, setSelectedYear] = useState("");
const downloadExcel = async () => {
try {
//updateIsLoading(true);
const res = await axiosInstance.get(`/${selectedCuestionario}`, {
params: { year: selectedYear, version },
headers: { Authorization: `Bearer ${admin.token}` },
responseType: "blob",
});
//saveAs(res.data, `${selectedYear}_${selectedCuestionario}_${version}.csv`);
// reset
setSelectedYear("");
setSelectedCuestionario("");
setVersion("");
//updateIsLoading(false);
} catch (err: any) {
//updateIsLoading(false);
//imprimirError(err.response?.data || "Error al descargar");
}
};
return (
<div>
<h4 className="mb-2">Descargar Cuestionario</h4>
<div>
<Col>
<FormGroup>
<FormLabel>Cuestionario:</FormLabel>
<InputGroup>
<FormSelect>
<option value="cuestionario_alumno">Cuestionario de Alumnos</option>
<option value="cuestionario_programa">Cuestionario de Programas</option>
</FormSelect>
</InputGroup>
</FormGroup>
</Col>
<Col>
<FormGroup>
<FormLabel>Version:</FormLabel>
<InputGroup>
<FormSelect>
<option value="v1">V1</option>
<option value="v2">V2</option>
</FormSelect>
</InputGroup>
</FormGroup>
</Col>
<Col>
<FormGroup>
<FormLabel>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>
<div>
<Button
variant="info"
onClick={downloadExcel}
>
Enviar archivo
</Button>
</div>
</div>
</div>
);
}
+113
View File
@@ -0,0 +1,113 @@
"use client";
import { useState } from "react";
import { axiosInstance } from "@/api/config";
import { Button, Col, FormGroup, FormLabel, InputGroup } from "react-bootstrap";
import DatePicker from "react-datepicker";
import moment from "moment";
//import { saveAs } from "file-saver";
interface Props {
admin: { token: string };
imprimirError?: (msg: string) => void;
updateIsLoading?: (loading: boolean) => void;
}
export default function Reporte({ admin }: Props) {
const [selectedInicio, setSelectedInicio] = useState<Date | null>(null);
const [selectedFin, setSelectedFin] = useState<Date | null>(null);
const minDate = new Date("2020-01-01");
const maxDate = new Date();
const validarFechas = () => {
const fechaInicio = moment(selectedInicio);
const fechaFin = moment(selectedFin);
if (!fechaInicio.isValid() || !fechaFin.isValid()) return true;
return false;
};
const descargar = async () => {
try {
//updateIsLoading(true);
const res = await axiosInstance.get("/servicio/reporte", {
params: {
inicio: moment(selectedInicio).format("YYYY-MM-DD"),
fin: moment(selectedFin).format("YYYY-MM-DD"),
},
headers: {
Authorization: `Bearer ${admin.token}`,
},
responseType: "blob",
});
//saveAs(res.data, "reporte.csv");
// reset
setSelectedInicio(null);
setSelectedFin(null);
//updateIsLoading(false);
} catch (err: any) {
//updateIsLoading(false);
//imprimirError(err.response?.data || "Error al descargar");
}
};
return (
<div>
<h4 className="mb-2">Descargar reporte</h4>
<label htmlFor="fecha" className="text-sm font-medium">
Selecciona una fecha de inicio
</label>
<input
type="date"
id="fecha"
className="w-[280px] rounded-md border border-gray-300 p-2 text-sm focus:border-blue-500 focus:ring focus:ring-blue-200"
/>
<p className="text-xs text-gray-500">fecha seleccionada: </p>
{/*
<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="yyyy-MM-dd"
className="form-control"
/>
</InputGroup>
</FormGroup>
</Col>
<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>
</FormGroup>
</Col>
*/}
<div>
<Button variant="info" onClick={descargar} disabled={validarFechas()}>
Descargar Excel
</Button>
</div>
</div>
);
}