From 5e13d3424a1f43b8955c0babd45f71990ae2befa Mon Sep 17 00:00:00 2001 From: IO420 <320154041@pcpuma.acatlan.unam.mx> Date: Wed, 10 Dec 2025 13:13:47 -0600 Subject: [PATCH] added new restriction and fixed button dowload --- public/excel.svg | 1 + src/app/styles/layout/BarNavigation.scss | 4 + src/app/styles/layout/pregunta7.scss | 5 +- src/components/AgregarEquipo.tsx | 61 +-- src/components/BarcodeScanner.tsx | 11 +- src/components/Dowload/Reporte.tsx | 25 +- src/components/Dowload/style.module.css | 41 +- src/components/Equipo_Computo/Pregunta2.tsx | 58 ++- src/components/Equipo_Computo/Pregunta3.tsx | 414 +++++++++++------- .../Equipo_Computo/pregunta2.module.scss | 11 +- .../Equipo_Computo/pregunta3.module.scss | 13 +- src/components/Equipo_Computo/pregunta9.css | 2 +- src/data/procesadores.ts | 5 +- src/data/so_por_equipo.ts | 6 +- 14 files changed, 427 insertions(+), 230 deletions(-) create mode 100644 public/excel.svg diff --git a/public/excel.svg b/public/excel.svg new file mode 100644 index 0000000..1d47514 --- /dev/null +++ b/public/excel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/app/styles/layout/BarNavigation.scss b/src/app/styles/layout/BarNavigation.scss index caf6184..08ff171 100644 --- a/src/app/styles/layout/BarNavigation.scss +++ b/src/app/styles/layout/BarNavigation.scss @@ -125,6 +125,10 @@ width: 100%; } + .barNavigation li{ + width: 100%; + } + .barNavigation ul { position: absolute; background-color: #f4f5f7; diff --git a/src/app/styles/layout/pregunta7.scss b/src/app/styles/layout/pregunta7.scss index b99c81c..742536c 100644 --- a/src/app/styles/layout/pregunta7.scss +++ b/src/app/styles/layout/pregunta7.scss @@ -59,6 +59,7 @@ border-radius: 0 0 4px 4px; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08); overflow: hidden; + min-height: 243px; } table { @@ -71,7 +72,7 @@ thead th { color: #fff; text-align: left; padding: 14px; - font-size: 15px; + font-size: 12px; font-weight: 600; cursor: pointer; user-select: none; @@ -85,7 +86,7 @@ thead th { tbody td { padding: 12px 14px; border-bottom: 1px solid #f0f0f0; - font-size: 15px; + font-size: 12px; } tr:nth-child(even) { diff --git a/src/components/AgregarEquipo.tsx b/src/components/AgregarEquipo.tsx index 4082309..70bc00c 100644 --- a/src/components/AgregarEquipo.tsx +++ b/src/components/AgregarEquipo.tsx @@ -373,6 +373,18 @@ export default function Page() { return; } + const inventario = formData.inventario; + + if (/^0/.test(inventario)) { + toast.error("El inventario no puede iniciar con 0"); + return; + } + + if (!/^[0-9E]+$/.test(inventario)) { + toast.error("El inventario solo puede contener n煤meros y la letra E"); + return; + } + try { const token = Cookies.get("token"); const headers = { Authorization: `Bearer ${token}` }; @@ -538,33 +550,30 @@ export default function Page() { )} -
- - + handleInputChange("id_sistema_operativo", e.target.value) + } + > + + {SO_POR_EQUIPO[formData.id_tipo_equipo]?.map((so) => ( + - {SO_POR_EQUIPO[formData.id_tipo_equipo]?.map((so) => ( - - ))} - -
+ ))} + + )} diff --git a/src/components/BarcodeScanner.tsx b/src/components/BarcodeScanner.tsx index 28de166..cdcd05d 100644 --- a/src/components/BarcodeScanner.tsx +++ b/src/components/BarcodeScanner.tsx @@ -19,7 +19,6 @@ export default function BarcodeScanner({ onScan }: BarcodeScannerProps) { let zxingReader: BrowserMultiFormatReader | null = null; let detector: any = null; - /** 馃殌 Intentar usar BarcodeDetector API (moderno) */ const startBarcodeDetector = async () => { const supported = "BarcodeDetector" in window; if (!supported) { @@ -100,13 +99,10 @@ export default function BarcodeScanner({ onScan }: BarcodeScannerProps) { return; } - // 馃攳 Intentar encontrar la c谩mara trasera const backCamera = - devices.find((d) => - d.label.toLowerCase().includes("back") - ) || devices.find((d) => - d.label.toLowerCase().includes("rear") - ) || devices[devices.length - 1]; // fallback + devices.find((d) => d.label.toLowerCase().includes("back")) || + devices.find((d) => d.label.toLowerCase().includes("rear")) || + devices[devices.length - 1]; const selectedDeviceId = backCamera.deviceId; @@ -136,7 +132,6 @@ export default function BarcodeScanner({ onScan }: BarcodeScannerProps) { startBarcodeDetector(); - return () => { if (animationFrame) cancelAnimationFrame(animationFrame); if (stream) stream.getTracks().forEach((t) => t.stop()); diff --git a/src/components/Dowload/Reporte.tsx b/src/components/Dowload/Reporte.tsx index 90511be..f14187f 100644 --- a/src/components/Dowload/Reporte.tsx +++ b/src/components/Dowload/Reporte.tsx @@ -2,15 +2,20 @@ import styles from "./style.module.css"; import Cookies from "js-cookie"; +import { useState } from "react"; export default function DownloadReporteXLSX() { + const [loading, setLoading] = useState(false); + const handleDownload = async () => { - const token = Cookies.get("token"); - const headers = { Authorization: `Bearer ${token}` }; + setLoading(true); // 馃敼 Activa loader + const token = Cookies.get("token"); + const headers = { Authorization: `Bearer ${token}` }; try { const response = await fetch( - `${process.env.NEXT_PUBLIC_API_URL}/equipos/reporteXLSX`,{headers} + `${process.env.NEXT_PUBLIC_API_URL}/equipos/reporteXLSX`, + { headers } ); if (!response.ok) { @@ -31,12 +36,22 @@ export default function DownloadReporteXLSX() { } catch (error) { console.error(error); alert("Hubo un problema al descargar el archivo."); + } finally { + setLoading(false); // 馃敼 Reactiva bot贸n y oculta loader } }; return ( - ); } diff --git a/src/components/Dowload/style.module.css b/src/components/Dowload/style.module.css index 84d44ad..b95adaf 100644 --- a/src/components/Dowload/style.module.css +++ b/src/components/Dowload/style.module.css @@ -1,24 +1,43 @@ .downloadBtn { display: flex; + justify-content: center; align-items: center; - background: #217346; - color: white; - border: none; - padding: 10px 16px; + gap: 8px; + background: #ffffff; + border: 1px solid #ddd; + border-bottom: none; + padding: 5px 5px; cursor: pointer; border-radius: 10px 10px 0 0; font-size: 14px; font-weight: 600; transition: background 0.2s ease; + min-width: 50px; } .downloadBtn:hover { - background: #1e5838; + background: #95d2b0; } -/* Icono con css puro */ -.icon { - width: 18px; - height: 18px; - position: relative; -} \ No newline at end of file +.downloadBtn:disabled { + background: #a5a5a5; + cursor: not-allowed; +} + +.loader { + border: 3px solid #ffffff55; + border-top: 3px solid white; + border-radius: 50%; + width: 16px; + height: 16px; + animation: spin 0.8s linear infinite; +} + +@keyframes spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} diff --git a/src/components/Equipo_Computo/Pregunta2.tsx b/src/components/Equipo_Computo/Pregunta2.tsx index 528c5f0..0040210 100644 --- a/src/components/Equipo_Computo/Pregunta2.tsx +++ b/src/components/Equipo_Computo/Pregunta2.tsx @@ -43,7 +43,7 @@ const PLATFORM_OS_LIST: Record = { ], "apple-desktop": [ - "MAC OS(15 - Sequoia)", + "MAC OS (SEQUOIA)", "Mac OS (13 - Ventura, 14 - Sonoma)", "Mac OS X (Mojave, Catalina, 11 - Big Sur, 12 - Monterrey)", "Mac OS X (Snow Leopard, Lion, Mountain Lion, Mavericks)", @@ -75,31 +75,49 @@ const PLATFORM_OS_LIST: Record = { ], }; +// reutilizable para normalizar comparaciones +function cleanName(str: string) { + return ( + str + .normalize?.("NFKD") + .replace?.(/\u00A0/g, " ") + .replace(/\s+/g, " ") + .trim?.() ?? str + ); +} + function transformPlatform( raw: RawOsEntry[], platform: PlatformKey ): OsEntry[] { const fixedList = PLATFORM_OS_LIST[platform]; - const clean = (str: string) => - str - .normalize?.("NFKD") - .replace?.(/\u00A0/g, " ") - .trim?.() ?? str; - + // mapa limpio de los datos que llegan const mapRaw: Record = {}; raw.forEach((item) => { - const name = clean(item.sistema_operativo); - mapRaw[name] = Number(item.total); + const name = cleanName(item.sistema_operativo); + mapRaw[name] = (mapRaw[name] || 0) + Number(item.total || 0); }); + // 1) primero los fijos, con su conteo (0 si no vienen) const rows: OsEntry[] = fixedList.map((os) => ({ os, - count: mapRaw[clean(os)] ?? 0, + count: mapRaw[cleanName(os)] ?? 0, })); - const total = rows.reduce((a, r) => a + r.count, 0); + // 2) luego agregamos cualquier SO extra que vino en los datos y no est茅 en la lista fija + const fixedCleanSet = new Set(fixedList.map((s) => cleanName(s))); + const extras = Object.keys(mapRaw) + .filter((rawName) => !fixedCleanSet.has(cleanName(rawName))) + // opcional: mantener orden natural o por nombre + .sort((a, b) => a.localeCompare(b, undefined, { sensitivity: "base" })) + .map((name) => ({ os: name, count: mapRaw[name] })); + // append extras after fixed ones + rows.push(...extras); + + // total (incluye extras) + const total = rows.reduce((a, r) => a + r.count, 0); rows.push({ os: "Total", count: total, isTotal: true }); return rows; @@ -159,6 +177,15 @@ export default function Pregunta2() { ? data[activeTab] : PLATFORM_OS_LIST[activeTab].map((os) => ({ os, count: 0 })); + const isOsInvalid = (os: string, tab: PlatformKey) => { + if (os === "Total") return false; + const fixedList = PLATFORM_OS_LIST[tab]; + // comparamos con cleaned strings + return !fixedList.some( + (s) => cleanName(s).toLowerCase() === cleanName(os).toLowerCase() + ); + }; + return (
@@ -175,7 +202,6 @@ export default function Pregunta2() {
- {/* Tabs */}
{Object.entries(PLATFORM_LABELS).map(([key, label]) => (