diff --git a/dev-dist/sw.js b/dev-dist/sw.js
index 8f413b5..8018ddf 100644
--- a/dev-dist/sw.js
+++ b/dev-dist/sw.js
@@ -79,7 +79,7 @@ define(['./workbox-fde070c5'], (function (workbox) { 'use strict';
*/
workbox.precacheAndRoute([{
"url": "/offline.html",
- "revision": "0.pef915uabhg"
+ "revision": "0.f6jp5lmvjvg"
}], {});
workbox.cleanupOutdatedCaches();
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("/offline.html"), {
diff --git a/package-lock.json b/package-lock.json
index 9165d8e..cea14e7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8593,7 +8593,7 @@
},
"node_modules/dayjs": {
"version": "1.11.13",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/debug": {
@@ -13269,6 +13269,13 @@
"jiti": "bin/jiti.js"
}
},
+ "node_modules/jquery": {
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
+ "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==",
+ "license": "MIT",
+ "peer": true
+ },
"node_modules/js-tokens": {
"version": "4.0.0",
"license": "MIT"
@@ -20600,6 +20607,21 @@
"is-typedarray": "^1.0.0"
}
},
+ "node_modules/typescript": {
+ "version": "4.9.5",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
+ "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peer": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=4.2.0"
+ }
+ },
"node_modules/unbox-primitive": {
"version": "1.0.2",
"dev": true,
diff --git a/package.json b/package.json
index 29e3b5c..75d4583 100644
--- a/package.json
+++ b/package.json
@@ -9,9 +9,8 @@
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview",
"test": "vitest",
- "test:unit": "vitest",
+ "test:unit": "vitest",
"cypress:run": "cypress run"
-
},
"dependencies": {
"@emotion/react": "^11.11.3",
diff --git a/src/views/Admin/reportes.jsx b/src/views/Admin/reportes.jsx
index 76a7537..36072d5 100644
--- a/src/views/Admin/reportes.jsx
+++ b/src/views/Admin/reportes.jsx
@@ -1,30 +1,55 @@
import React, { useState, useEffect } from "react";
import { Pie } from "react-chartjs-2";
-import { Chart as ChartJS, Title, Tooltip, Legend, ArcElement, CategoryScale, LinearScale } from "chart.js";
-
-ChartJS.register(Title, Tooltip, Legend, ArcElement, CategoryScale, LinearScale);
+import {
+ Chart as ChartJS,
+ Title,
+ Tooltip,
+ Legend,
+ ArcElement,
+ CategoryScale,
+ LinearScale,
+} from "chart.js";
+
+ChartJS.register(
+ Title,
+ Tooltip,
+ Legend,
+ ArcElement,
+ CategoryScale,
+ LinearScale
+);
function ResultadosEncuestas() {
- const [resultados, setResultados] = useState([]);
- const [totalPersonas, setTotalPersonas] = useState(0); // Nuevo estado para personas únicas
- const [cargando, setCargando] = useState(true);
+ const [resultados, setResultados] = useState([]); // Datos de las encuestas
+ const [totalPersonas, setTotalPersonas] = useState(0); // Total de personas que han respondido
+ const [cargando, setCargando] = useState(true); // Estado de carga
+
+ const mapeoPreguntas = {
+ question1: "¿Qué tan fácil fue encontrar la información que buscabas?",
+ question2: "¿Cómo calificarías la facilidad de uso del sistema para agendar tu cita?",
+ question3: "¿Qué tan satisfecho estás con el proceso de agendar una cita?",
+ question4: "¿Qué tan rápido te pareció el sistema para agendar tu cita?",
+ question5: "¿Qué tan claro fue el mensaje de confirmación de tu cita?",
+ };
useEffect(() => {
+ // Obtener los resultados de las encuestas
const obtenerResultados = async () => {
try {
- const response = await fetch("http://localhost:3000/resultados", {
- method: "GET",
- headers: { "Content-Type": "application/json" },
- });
+ const response = await fetch(
+ "https://backopt-production.up.railway.app/feedback/obtenerResultadosEncuestas",
+ {
+ method: "GET",
+ headers: { "Content-Type": "application/json" },
+ }
+ );
if (response.ok) {
const data = await response.json();
-
if (data) {
- setResultados(data.data || []); // Manejo de resultados de encuestas
- setTotalPersonas(data.totalPersonas || 0); // Guardar el total de personas únicas
+ setResultados(data.data || []); // Maneja los resultados de las encuestas
+ setTotalPersonas(data.totalPersonas || 0); // Guarda el total de personas únicas
} else {
- console.error("La respuesta no contiene datos válidos");
setResultados([]);
setTotalPersonas(0);
}
@@ -32,14 +57,14 @@ function ResultadosEncuestas() {
alert("Error al obtener los resultados");
}
} catch (error) {
- console.error("Error al obtener los resultados:", error);
alert("Hubo un error al obtener los resultados");
+ console.error("Error al obtener los resultados:", error);
} finally {
- setCargando(false);
+ setCargando(false); // Finaliza la carga
}
};
- obtenerResultados();
+ obtenerResultados(); // Llamar la función de obtención de resultados
}, []);
if (cargando) {
@@ -47,32 +72,33 @@ function ResultadosEncuestas() {
}
const procesarDatos = () => {
- const respuestasPorPregunta = {};
-
- resultados.forEach((encuesta) => {
- const { pregunta, respuestas } = encuesta;
-
- if (!respuestasPorPregunta[pregunta]) {
- respuestasPorPregunta[pregunta] = { "1": 0, "2": 0, "3": 0, "4": 0, "5": 0 };
- }
-
- Object.entries(respuestas).forEach(([calificacion, cantidad]) => {
- respuestasPorPregunta[pregunta][calificacion] += cantidad;
+ if (typeof resultados !== "object" || Array.isArray(resultados)) {
+ console.error("Los resultados no son un objeto:", resultados);
+ return { labels: [], datasets: [] };
+ }
+
+ const respuestasPorPregunta = [];
+
+ Object.entries(resultados).forEach(([pregunta, respuestas]) => {
+ const preguntaReal = mapeoPreguntas[pregunta] || pregunta; // Usa el mapeo o la clave por defecto
+ const respuestasArray = Object.values(respuestas);
+
+ respuestasPorPregunta.push({
+ label: preguntaReal,
+ data: respuestasArray,
+ backgroundColor: [
+ "#FF6F61",
+ "#6B5B95",
+ "#88B04B",
+ "#F7CAC9",
+ "#92A8D1",
+ ],
});
});
- const labels = Object.keys(respuestasPorPregunta);
- const datasets = labels.map((pregunta) => {
- return {
- label: pregunta,
- data: Object.values(respuestasPorPregunta[pregunta]),
- backgroundColor: ["#FF6F61", "#6B5B95", "#88B04B", "#F7CAC9", "#92A8D1"],
- };
- });
-
return {
- labels: ["1", "2", "3", "4", "5"], // Mantenemos las etiquetas originales (números)
- datasets,
+ labels: ["1", "2", "3", "4", "5"],
+ datasets: respuestasPorPregunta,
};
};
@@ -83,7 +109,7 @@ function ResultadosEncuestas() {
Resultados de Encuestas Web
- {/* Mostrar cantidad de personas únicas que han respondido */}
+ {/* Mostrar el total de personas que han respondido */}
Total de personas que han respondido: {totalPersonas}
@@ -93,7 +119,10 @@ function ResultadosEncuestas() {
) : (
{data.datasets.map((dataset, index) => (
-
+
{dataset.label}
{
- const calificacion = estrellas[tooltipItem.dataIndex]; // Convertir índice a estrellas
+ const calificacion =
+ estrellas[tooltipItem.dataIndex]; // Convertir índice a estrellas
const value = dataset.data[tooltipItem.dataIndex];
return `${calificacion}: Personas que han respondido: ${value}`;
},
diff --git a/src/views/feedback/encuesta.jsx b/src/views/feedback/encuesta.jsx
index 2ee8801..e760391 100644
--- a/src/views/feedback/encuesta.jsx
+++ b/src/views/feedback/encuesta.jsx
@@ -1,4 +1,4 @@
-import React, { useState, useEffect } from "react";
+import React, { useState, useEffect, useRef } from "react";
import { useNavigate } from "react-router-dom";
// Función para decodificar JWT
@@ -24,6 +24,9 @@ function EncuestaCitas() {
const [encuestaCompletada, setEncuestaCompletada] = useState(false); // Estado para verificar si ya se completó la encuesta
const navigate = useNavigate();
+ // Usamos un useRef para asegurarnos de que registrarAcceso se ejecute solo una vez
+ const accesoRegistrado = useRef(false);
+
const preguntas = [
"¿Qué tan fácil fue encontrar la información que buscabas?",
"¿Cómo calificarías la facilidad de uso del sistema para agendar tu cita?",
@@ -33,40 +36,53 @@ function EncuestaCitas() {
];
useEffect(() => {
- // Recuperar el token del localStorage
const token = localStorage.getItem("token");
if (token) {
- const decodedToken = parseJwt(token);
- const idUsuario = decodedToken.clienteId; // Asumimos que el idUsuario está en el campo `id`
-
- // Verificar si la encuesta ya fue completada
- const verificarEncuesta = async () => {
- try {
- const response = await fetch(`http://localhost:3000/Encuesta/completada?idUsuario=${idUsuario}`, {
- method: "GET",
- headers: { "Content-Type": "application/json" },
- });
-
- if (response.ok) {
- const data = await response.json();
- if (data.completada) {
- setEncuestaCompletada(true); // Establecer que la encuesta ya fue completada
- }
- }
- } catch (error) {
- console.error("Error al verificar encuesta:", error);
- } finally {
- setCargando(false); // Finaliza el estado de carga
+ const decodedToken = parseJwt(token);
+ console.log(decodedToken); // Verifica el contenido del token
+
+ const idUsuario = decodedToken.clienteId;
+
+ if (!idUsuario) {
+ console.error("El idUsuario no se pudo obtener del token.");
+ return; // Salir si no hay idUsuario
}
- };
- verificarEncuesta();
+ // Evitar llamar registrarAcceso si ya fue completado
+ if (!accesoRegistrado.current && !encuestaCompletada) {
+ const registrarAcceso = async () => {
+ try {
+ const response = await fetch("https://backopt-production.up.railway.app/feedback/acceso", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ idUsuario }),
+ });
+
+ if (response.ok) {
+ const data = await response.json();
+ if (data.estado === "Encuesta ya completada") {
+ setEncuestaCompletada(true); // Marcar como completada si ya está hecho
+ }
+ } else {
+ console.error("Error al registrar el acceso al feedback");
+ }
+ } catch (error) {
+ console.error("Error al registrar acceso:", error);
+ } finally {
+ setCargando(false); // Finaliza el estado de carga
+ }
+ };
+
+ registrarAcceso(); // Solo ejecutar si la encuesta no está completada
+ accesoRegistrado.current = true; // Marcar como registrado
+ } else {
+ setCargando(false); // Si ya está completada, no hacer nada
+ }
} else {
- // Si no hay token, redirigir al login o manejar el error
- navigate("/login");
+ navigate("/login"); // Redirige al login si no hay token
}
- }, [navigate]);
+}, [encuestaCompletada, navigate]); // Solo ejecutará cuando cambie `encuestaCompletada`
const handleRespuestaChange = (index, value) => {
setRespuestas((prev) => ({
@@ -87,14 +103,22 @@ function EncuestaCitas() {
const decodedToken = parseJwt(token);
const idUsuario = decodedToken.clienteId;
+ if (!idUsuario) {
+ alert("No se pudo obtener el idUsuario del token.");
+ return; // Salir si no hay idUsuario
+ }
+
try {
- const response = await fetch("http://localhost:3000/Encuesta", {
- method: "POST",
+ const response = await fetch("https://backopt-production.up.railway.app/feedback/completar", {
+ method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
- idUsuario, // Usar el id del usuario decodificado del JWT
- respuestas,
- preguntas, // Envía las preguntas al backend
+ idUsuario,
+ question1: respuestas[0],
+ question2: respuestas[1],
+ question3: respuestas[2],
+ question4: respuestas[3],
+ question5: respuestas[4],
}),
});
diff --git a/src/views/inicio.jsx b/src/views/inicio.jsx
index f55f556..b0c77e2 100644
--- a/src/views/inicio.jsx
+++ b/src/views/inicio.jsx
@@ -1,16 +1,41 @@
-import { useState } from "react";
-/* import Navbar from "../components/BarraNavegacion"; */
+import { useState, useEffect } from "react";
+import Barra from "../components/Navegacion/barra";
import Slider from "../home/slider";
import Fot from "../components/Footer";
+import Scrool from "../components/scroll";
import imagen from "../img/Venta.png";
import imagen2 from "../img/lentes2.png";
-import Scrool from '../components/scroll';
-import Barra from "../components/Navegacion/barra";
import lente from "../img/3d.jpg";
-
-
+import Encuesta from "../views/feedback/encuesta";
+
+// Función para decodificar el JWT y obtener el clienteId
+function getClienteIdFromToken() {
+ const token = localStorage.getItem("token");
+
+ if (token) {
+ const base64Url = token.split(".")[1];
+ const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
+ const jsonPayload = decodeURIComponent(
+ window
+ .atob(base64)
+ .split("")
+ .map(function (c) {
+ return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
+ })
+ .join("")
+ );
+ const decodedToken = JSON.parse(jsonPayload);
+ return decodedToken.clienteId;
+ }
+
+ return null;
+}
function App() {
+ const [mostrarEncuesta, setMostrarEncuesta] = useState(false); // Estado para la encuesta pendiente
+ const [idUsuario, setIdUsuario] = useState(null); // Estado para idUsuario (inicializado como null)
+ const [encuesta, setEncuesta] = useState(null); // Estado para almacenar la información de la encuesta
+
const [mostrarElemento, setMostrarElemento] = useState(false);
const [mostrarElemento2, setMostrarElemento2] = useState(false);
const [mostrarElemento3, setMostrarElemento3] = useState(false);
@@ -42,180 +67,105 @@ function App() {
setIsZoomed3(false);
};
+ // Función para verificar si el usuario tiene una encuesta pendiente
+ const obtenerEncuestaPendiente = async (idUsuario) => {
+ try {
+ const response = await fetch("https://backopt-production.up.railway.app/feedback/pendiente", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({ idUsuario }),
+ });
+
+ const data = await response.json();
+
+ if (data.estado === "Pendiente") {
+ return {
+ estado: "Pendiente",
+ id_encuesta: data.id_encuesta,
+ mensaje: data.mensaje,
+ };
+ } else {
+ return { estado: "Completado", mensaje: data.mensaje };
+ }
+ } catch (error) {
+ console.error("Error al obtener la encuesta pendiente:", error);
+ return {
+ estado: "Error",
+ mensaje: "No se pudo verificar la encuesta pendiente.",
+ };
+ }
+ };
+
+ useEffect(() => {
+ const id = getClienteIdFromToken();
+ if (id) {
+ setIdUsuario(id);
+ } else {
+ console.error("No se pudo obtener el idUsuario del token.");
+ }
+ }, []);
+
+ useEffect(() => {
+ if (idUsuario) {
+ const verificarEncuesta = async () => {
+ const encuesta = await obtenerEncuestaPendiente(idUsuario);
+ if (encuesta.estado === "Pendiente") {
+ setEncuesta(encuesta); // Guardamos la encuesta pendiente
+ setMostrarEncuesta(true); // Mostramos la encuesta
+ }
+ };
+ verificarEncuesta();
+ }
+ }, [idUsuario]);
+
return (
<>
-{/* */}
+
-
-
+ {mostrarEncuesta && encuesta && (
+
+
+
+ )}
+
+ {/* Contenido de la página */}
-
+
+
+
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
- The Coldest Sunset
-
-
- Estos lentes ofrecen un estilo único y moderno que te hará
- destacar en cualquier ocasión. Con su diseño elegante y
- funcional, podrás disfrutar de la máxima protección UV y una
- visión nítida y clara en todo momento.
-
- {mostrarElemento && (
-
- Su montura ligera y resistente garantiza una comodidad
- duradera, mientras que sus cristales de alta calidad te
- brindan una visión sin igual. ¡Prepárate para lucir
- increíble con los lentes The Coldest Sunset!
-
- )}
-
-
-
-
-
-
-
-
-
-
- {/* Aquí empieza El segundo */}
-
-
-
-
- The Coldest Sunset
-
-
- Estos lentes ofrecen un estilo único y moderno que te hará
- destacar en cualquier ocasión. Con su diseño elegante y
- funcional, podrás disfrutar de la máxima protección UV y una
- visión nítida y clara en todo momento.
-
- {mostrarElemento2 && (
-
- Su montura ligera y resistente garantiza una comodidad
- duradera, mientras que sus cristales de alta calidad te
- brindan una visión sin igual. ¡Prepárate para lucir
- increíble con los lentes The Coldest Sunset!
-
- )}
-
-
-
-
-
-
- {/* Aquí empieza el tercero */}
-
+
-
-
- The Coldest Sunset
-
-
- Estos lentes ofrecen un estilo único y moderno que te hará
- destacar en cualquier ocasión. Con su diseño elegante y
- funcional, podrás disfrutar de la máxima protección UV y una
- visión nítida y clara en todo momento.
-
- {mostrarElemento3 && (
-
- Su montura ligera y resistente garantiza una comodidad
- duradera, mientras que sus cristales de alta calidad te
- brindan una visión sin igual. ¡Prepárate para lucir
- increíble con los lentes The Coldest Sunset!
-
- )}
-
-
-
-
-
-
-
-
-
+
+
+ ¡Estás a un paso de obtener lo que necesitas!
+
+
+ Encuentra tus productos ópticos favoritos y realiza tu compra de
+ forma fácil y rápida.
+