Merge branch 'Lino' of https://github.com/jls846/front-censo into Sarabia

This commit is contained in:
jls846
2025-11-27 17:36:39 -06:00
11 changed files with 205 additions and 41 deletions
+3 -19
View File
@@ -7,40 +7,28 @@ import Cookies from "js-cookie";
import "../../styles/layout/ForgotPasswordPage.scss";
export default function UpdateUserPage() {
const [id_User, setId_User] = useState("");
const [nombre, setNombre] = useState("");
const [contrasenaN, setContrasenaN] = useState("");
const [contrasenaV, setContrasenaV] = useState("");
const [tipoUsuario, setTipoUsuario] = useState("");
const [loading, setLoading] = useState(false);
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (!id_User || isNaN(Number(id_User))) {
toast.error("El ID de usuario es obligatorio y debe ser un número.");
return;
}
if (nombre && nombre.trim().length < 3) {
if (!nombre || nombre.trim().length < 3) {
toast.error("El nombre debe tener al menos 3 caracteres.");
return;
}
if (contrasenaN && contrasenaN.length < 6) {
if (!contrasenaN || contrasenaN.length < 6) {
toast.error("La nueva contraseña debe tener al menos 6 caracteres.");
return;
}
if (contrasenaV && contrasenaV.length < 6) {
if (!contrasenaV || contrasenaV.length < 6) {
toast.error("La contraseña actual debe tener al menos 6 caracteres.");
return;
}
if (tipoUsuario && isNaN(Number(tipoUsuario))) {
toast.error("El tipo de usuario debe ser un número.");
return;
}
try {
setLoading(true);
@@ -50,11 +38,9 @@ export default function UpdateUserPage() {
const response = await axios.post(
`${process.env.NEXT_PUBLIC_API_URL}/auth/UpdateUsuario`,
{
id_User: Number(id_User),
nombre,
contrasenaN,
contrasenaV,
tipoUsuario: tipoUsuario ? Number(tipoUsuario) : undefined,
},
{ headers }
);
@@ -71,8 +57,6 @@ export default function UpdateUserPage() {
<section className="forgot-password-page">
<div className="forgot-form-container">
<h1>Actualizar contraseña</h1>
<p>Ingresa el ID y los campos que deseas modificar.</p>
<form onSubmit={handleSubmit} className="forgot-form">
<div>
-2
View File
@@ -109,7 +109,6 @@
border-radius: 6px;
cursor: pointer;
font-weight: bold;
font-size: 2rem;
display: flex;
align-items: center;
justify-content: center;
@@ -171,7 +170,6 @@
.logout-button {
margin-top: 10px;
font-size: 1.75rem;
padding: 6px 12px;
max-width: 180px;
}
@@ -109,7 +109,6 @@
border-radius: 6px;
cursor: pointer;
font-weight: bold;
font-size: 2rem;
display: flex;
align-items: center;
justify-content: center;
@@ -170,7 +169,6 @@
.logout-button {
margin-top: 10px;
font-size: 1.75rem;
padding: 6px 12px;
max-width: 180px;
}
@@ -9,7 +9,6 @@
justify-content: center;
align-items: center;
width: 100%;
min-height: 100vh;
padding: 2rem;
color: #222;
@@ -26,7 +25,7 @@
font-size: 3rem;
font-weight: 700;
margin-bottom: 1.5rem;
color: #bd8c01; // Amarillo tipo login
color: #bd8c01;
}
p {
+22 -11
View File
@@ -17,10 +17,11 @@ type RespuestaAntiguedad = {
export default function Pregunta7() {
const [datos, setDatos] = useState([
{ nombre: "Impresión", valores: ["0.00", "0.00", "0.00", "0.00", "0.00"] },
{ nombre: "Impresión", valores: ["0.00", "0.00", "0.00", "0.00"] },
{
nombre: "Digitalización",
valores: ["0.00", "0.00", "0.00", "0.00", "0.00"],
valores: ["0.00", "0.00", "0.00", "0.00"
],
},
]);
@@ -40,7 +41,9 @@ export default function Pregunta7() {
for (const item of items) {
const total = Number(item.total) || 0;
const antig = item.antiguedad ? item.antiguedad.toUpperCase().trim() : null;
const antig = item.antiguedad
? item.antiguedad.toUpperCase().trim()
: null;
switch (antig) {
case "MENORES DE 2":
@@ -62,7 +65,7 @@ export default function Pregunta7() {
const total = menores2 + entre2_3 + entre4_5 + mayores6 + notfound;
if (total === 0) return ["0.00", "0.00", "0.00", "0.00", "0.00"];
if (total === 0) return ["0.00", "0.00", "0.00", "0.00"];
const pct = (v: number) => ((v / total) * 100).toFixed(2);
@@ -71,7 +74,6 @@ export default function Pregunta7() {
pct(entre2_3),
pct(entre4_5),
pct(mayores6),
pct(notfound),
];
};
@@ -93,7 +95,10 @@ export default function Pregunta7() {
setDatos([
{ nombre: "Impresión", valores: transformarDatos(impresion) },
{ nombre: "Digitalización", valores: transformarDatos(digitalizacion) },
{
nombre: "Digitalización",
valores: transformarDatos(digitalizacion),
},
]);
})
.catch((err) => console.error("Error cargando datos:", err))
@@ -103,11 +108,13 @@ export default function Pregunta7() {
const calcularTotalFila = (valores: string[]) =>
valores.reduce((t, v) => t + Number(v), 0);
if (loading) return <div className="contenedor-pregunta">Cargando datos...</div>;
return (
<div className="contenedor-pregunta">
<div className="pregunta-cuadro" style={{ marginTop: "30px" }}>
<div className="contenedor-censo">
Censo de equipos periféricos - Equipo de digitalización
</div>
<div className="pregunta-cuadro">
Antigüedad que tienen los equipos periféricos del área universitaria.
</div>
@@ -120,7 +127,7 @@ export default function Pregunta7() {
<th className="rosa-fuerte">Entre 2 y 3 años</th>
<th className="rosa-fuerte">Entre 4 y 5 años</th>
<th className="rosa-fuerte">Mayor a 6 años</th>
<th className="rosa-fuerte">No registrados</th>
<th className="azul-marino">Total</th>
</tr>
</thead>
@@ -142,7 +149,11 @@ export default function Pregunta7() {
</td>
))}
<td className={`total-celda ${total > 100.1 ? "total-error" : ""}`}>
<td
className={`total-celda ${
total > 100.1 ? "total-error" : ""
}`}
>
{total.toFixed(2)}%
</td>
</tr>
+3 -3
View File
@@ -1,5 +1,7 @@
.contenedor-pregunta {
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
display: flex;
flex-direction: column;
gap: 10px;
}
.contenedor-censo{
@@ -7,7 +9,6 @@
color: white;
padding: 15px;
font-size: 16px;
margin-bottom: 25px;
border-radius: 4px;
}
@@ -17,7 +18,6 @@
padding: 15px;
border-radius: 8px;
font-size: 16px;
margin-bottom: 25px;
}
.tabla-contenedor {
@@ -3,6 +3,7 @@ import React, { useState, useEffect } from "react";
import style from "./pregunta1EP.module.scss";
import axios from "axios";
import Cookies from "js-cookie";
import ToggleButton from "../Toggle/ToggleButton";
interface EquiposImpresion {
inyeccionTinta: string;
@@ -122,6 +123,7 @@ export default function Pregunta1EP() {
<div className="pregunta-cuadro">Equipos de impresión.</div>
<ToggleButton/>
<div className={style.grid}>
{/* Columna 1 */}
<div className="item">
@@ -1,4 +1,7 @@
.container_P2 {
display: flex;
flex-direction: column;
gap: 10px;
max-width: 1200px;
label {
+3 -2
View File
@@ -1,5 +1,7 @@
.container {
display: flex;
flex-direction: column;
gap: 10px;
width: 100%;
}
@@ -29,7 +31,6 @@
.item {
flex: 1;
min-width: 150px;
}
label {
@@ -0,0 +1,116 @@
:root {
--bg-off: #fff;
--bg-on: linear-gradient(90deg, #003e79 0%, #015cb8 100%);
--thumb-off: #015cb8;
--thumb-on: #fff;
--shadow: 0 6px 18px rgba(99, 102, 241, 0.18);
--curve: cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.containerToggle{
display: flex;
justify-content: end;
}
.toggle {
--padding: 4px;
display: inline-flex;
align-items: center;
gap: 12px;
border: none;
background: transparent;
cursor: pointer;
user-select: none;
border-radius: 999px;
transition: transform 160ms ease, box-shadow 160ms ease;
outline: none;
}
.toggle:active .track {
box-shadow: inset 0 0 0 2em rgba(0, 0, 0, 0.06);
}
.track {
width: 70px;
height: 34px;
background: var(--bg-off);
border-radius: 999px;
display: inline-block;
padding: var(--padding);
box-sizing: border-box;
position: relative;
transition: background 280ms var(--curve), box-shadow 220ms ease;
border: 1px solid #e8eae9;
}
.on .track {
background: var(--bg-on);
box-shadow: var(--shadow);
border-color: transparent;
}
.thumb {
width: 23px;
height: 23px;
background: var(--thumb-off);
border-radius: 50%;
display: block;
transform: translateX(0);
transition: transform 260ms var(--curve), background 240ms ease,
padding 260ms ease, margin 260ms ease, box-shadow 240ms ease;
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.12);
}
/* Rebote estilo iOS */
.toggle:active .thumb {
padding-right: 4px;
}
.on:active .thumb {
margin-left: -4px;
}
.on .thumb {
background: var(--thumb-on);
transform: translateX(36px);
box-shadow: 0 6px 16px rgba(91, 90, 255, 0.18);
}
.label {
position: absolute;
right: 10px;
top: 8px;
font-size: 14px;
font-weight: 600;
color: #0f172a;
letter-spacing: 0.2px;
transition: color 240ms ease, right 260ms var(--curve);
}
.on .label {
right: 45%;
color: #fff;
text-shadow: 0 1px 0 rgba(0, 0, 0, 0.12);
}
.toggle:focus-visible .track {
box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.12);
}
@media (max-width: 420px) {
.track {
width: 52px;
height: 30px;
}
.thumb {
width: 22px;
height: 22px;
}
.on .thumb {
transform: translateX(24px);
}
.label {
font-size: 13px;
}
}
+52
View File
@@ -0,0 +1,52 @@
// ToggleButton.tsx
// Usa este archivo como `ToggleButton.tsx`
import React, { useState } from "react";
import styles from "./ToggleButton.module.scss";
export type ToggleButtonProps = {
/** estado inicial: true -> presionado ("desuso") */
defaultOn?: boolean;
/** callback cuando cambia el estado */
onChange?: (on: boolean) => void;
/** texto cuando está apagado (por defecto: "uso") */
offLabel?: string;
/** texto cuando está encendido (por defecto: "desuso") */
onLabel?: string;
className?: string;
};
const ToggleButton: React.FC<ToggleButtonProps> = ({
defaultOn = false,
onChange,
offLabel = "Uso",
onLabel = "Baja",
className = "",
}) => {
const [on, setOn] = useState<boolean>(defaultOn);
function toggle() {
const next = !on;
setOn(next);
onChange?.(next);
}
return (
<section className={styles.containerToggle}>
<button
type="button"
aria-pressed={on}
className={`${styles.toggle} ${on ? styles.on : ""} ${className}`}
onClick={toggle}
title={on ? onLabel : offLabel}
>
<span className={styles.track}>
<span className={styles.thumb} />
<span className={styles.label}>{on ? onLabel : offLabel}</span>
</span>
</button>
</section>
);
};
export default ToggleButton;