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

This commit is contained in:
2025-10-27 17:44:25 -06:00
5 changed files with 825 additions and 93 deletions
+52 -35
View File
@@ -1,8 +1,13 @@
"use client";
import { useState } from "react";
import { useEffect, useState } from "react";
import "../../styles/layout/agregarEquipo.scss";
import "./editar.css";
import { useSearchParams } from "next/navigation";
export default function Home() {
const searchParams = useSearchParams();
const equipoId = searchParams.get("equipoId"); // 🔹 Obtener ID del equipo
const [formData, setFormData] = useState({
serie: "",
marca: "",
@@ -18,12 +23,19 @@ export default function Home() {
responsable: "",
});
// Si viene un equipoId, precargarlo
useEffect(() => {
if (equipoId) {
setFormData((prev) => ({ ...prev, serie: equipoId }));
}
}, [equipoId]);
// Estado para saber si mostramos los campos de SO y Procesador
const mostrarCamposComputadora = formData.tipoEquipo !== "Impresora";
const handleGuardar = () => {
console.log("Formulario enviado:", formData);
alert("Equipo guardado (vista solo)");
alert(`Equipo ${formData.serie} guardado (vista solo)`);
};
const handleCancelar = () => {
@@ -34,10 +46,11 @@ export default function Home() {
<div className="agregarEquipoContainer">
<div className="innerContainer">
<h2 className="information">
<span></span>
{equipoId ? `Editar Equipo (${equipoId})` : "Agregar Equipo"}
<span>Inventario:</span>
<span>Fecha de censo:</span>
</h2>
<form className="equipoForm">
{/* Columna 1 */}
<div className="column">
@@ -113,39 +126,43 @@ export default function Home() {
{/* Columna 2 */}
<div className="column">
<div className="formGroup">
<label>Procesador</label>
<select
value={formData.procesador}
onChange={(e) =>
setFormData({ ...formData, procesador: e.target.value })
}
>
<option value="">Selecciona procesador</option>
<option value="Ryzen 3">Ryzen 3</option>
<option value="Intel i5">Intel i5</option>
<option value="AMD Ryzen 5">AMD Ryzen 5</option>
</select>
</div>
{mostrarCamposComputadora && (
<>
<div className="formGroup">
<label>Procesador</label>
<select
value={formData.procesador}
onChange={(e) =>
setFormData({ ...formData, procesador: e.target.value })
}
>
<option value="">Selecciona procesador</option>
<option value="Ryzen 3">Ryzen 3</option>
<option value="Intel i5">Intel i5</option>
<option value="AMD Ryzen 5">AMD Ryzen 5</option>
</select>
</div>
<div className="formGroup">
<label>Sistema operativo</label>
<select
value={formData.sistemaOperativo}
onChange={(e) =>
setFormData({
...formData,
sistemaOperativo: e.target.value,
})
}
>
<option value="">Selecciona sistema operativo</option>
<option value="Windows 10">Windows 10</option>
<option value="Windows 11">Windows 11</option>
<option value="Linux">Linux</option>
<option value="macOS">macOS</option>
</select>
</div>
<div className="formGroup">
<label>Sistema operativo</label>
<select
value={formData.sistemaOperativo}
onChange={(e) =>
setFormData({
...formData,
sistemaOperativo: e.target.value,
})
}
>
<option value="">Selecciona sistema operativo</option>
<option value="Windows 10">Windows 10</option>
<option value="Windows 11">Windows 11</option>
<option value="Linux">Linux</option>
<option value="macOS">macOS</option>
</select>
</div>
</>
)}
<div className="formGroup">
<label>Adscripción</label>
+49 -26
View File
@@ -3,8 +3,8 @@
import { useState } from "react";
import { useRouter } from "next/navigation";
import "../../styles/layout/escaner.scss"; // Importación global
import BarcodeScanner from "@/components/BarcodeScanner";
// Tipos
type Equipo = {
id: string;
nombre: string;
@@ -18,59 +18,82 @@ const initialEquipos: Equipo[] = [
export default function Dashboard() {
const router = useRouter();
const [equipos, setEquipos] = useState<Equipo[]>(initialEquipos);
const [isScanning, setIsScanning] = useState(false);
const [equipos, setEquipos] = useState(initialEquipos);
const [search, setSearch] = useState("");
const [lastScan, setLastScan] = useState("");
const [lastScan, setLastScan] = useState<string | null>(null);
// 🔍 Buscar equipo manualmente
const buscarEquipo = () => {
const resultado = equipos.filter(
const encontrado = equipos.find(
(e) =>
e.id.includes(search) ||
e.nombre.toLowerCase().includes(search.toLowerCase())
e.id === search.trim() ||
e.nombre.toLowerCase() === search.trim().toLowerCase()
);
setEquipos(resultado);
};
const cerrarSesion = () => {
localStorage.removeItem("loggedIn");
router.push("/");
if (encontrado) {
// Redirige a la vista de edición con el ID del equipo
router.push(`/Editar?equipoId=${encontrado.id}`);
} else {
alert("Equipo no encontrado.");
}
};
// 📷 Cuando se escanea un código de barras
const handleScan = (code: string) => {
setLastScan(code);
setIsScanning(false);
const encontrado = equipos.find((e) => e.id === code);
if (encontrado) {
alert(`Equipo encontrado: ${encontrado.nombre} (${encontrado.estado})`);
// ✅ Si el equipo existe, redirige directamente
router.push(`/Editar?equipoId=${encontrado.id}`);
} else {
const codigo = prompt("Equipo no registrado. Ingresa el codigo:");
if (codigo) {
setEquipos([
...equipos,
{ id: code, nombre: codigo, estado: "Disponible" },
]);
// 🚫 Si no existe, se puede registrar
const nombre = prompt("Equipo no registrado. Ingresa el nombre:");
if (nombre) {
const nuevoEquipo = { id: code, nombre, estado: "Disponible" };
setEquipos([...equipos, nuevoEquipo]);
alert("Equipo agregado al inventario");
}
}
};
// 🔒 Cerrar sesión
const cerrarSesion = () => {
localStorage.removeItem("loggedIn");
router.push("/");
};
return (
<div className="dashboardContainer">
{/* VISTA DE ESCANEO */}
<main className="scanView">
<h3>Escanear Inventario</h3>
<div className="scannerFrame">
<img src="/barcode_scanner.png" alt="Marco de escaneo" />
</div>
{isScanning ? (
<BarcodeScanner onScan={handleScan} />
) : (
<div className="scannerFrame">
<img src="/barcode_scanner.png" alt="Marco de escaneo" />
</div>
)}
<p className="instructions">
Enfoca el código de barras dentro del marco
{isScanning
? "Enfoca el código de barras dentro del marco"
: "Presiona el botón para escanear"}
</p>
<button className="scanButton">
<img src="/photo_camera.png" alt="camara" /> Escanear
<button
className="scanButton"
onClick={() => setIsScanning(!isScanning)}
>
<img src="/photo_camera.png" alt="cámara" />
{isScanning ? "Cancelar" : "Escanear"}
</button>
{/* 🔍 Búsqueda manual */}
<div className="searchBox">
<input
type="text"
+101 -26
View File
@@ -1,42 +1,117 @@
// components/BarcodeScanner.tsx
"use client";
import { useEffect, useRef } from "react";
import { BrowserMultiFormatReader } from "@zxing/library";
import React, { useEffect, useRef } from "react";
import Quagga from "@ericblade/quagga2";
type Props = { onDetected: (code: string) => void; };
// Tipos mínimos para el resultado de Quagga
interface CodeResult {
code: string;
format: string;
}
export default function BarcodeScanner({ onDetected }: Props) {
const videoRef = useRef<HTMLVideoElement>(null);
interface QuaggaResult {
codeResult: CodeResult | null;
}
// Tipos para la configuración (opcional, pero mejora legibilidad)
interface QuaggaConfig {
inputStream: {
name: string;
type: string;
target: HTMLElement;
constraints: MediaTrackConstraints;
};
decoder: {
readers: string[];
};
locate: boolean;
}
interface BarcodeScannerProps {
onScan: (code: string) => void;
}
export default function BarcodeScanner({ onScan }: BarcodeScannerProps) {
const scannerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const codeReader = new BrowserMultiFormatReader();
if (!videoRef.current) return;
if (!scannerRef.current) return;
// Obtener la cámara trasera si existe
codeReader.listVideoInputDevices().then((devices) => {
const backDevice = devices.find(d => d.label.toLowerCase().includes("back"));
const deviceId = backDevice ? backDevice.deviceId : devices[0].deviceId;
// ✅ Sin `any`: usamos un objeto literal tipado implícitamente
const config: QuaggaConfig = {
inputStream: {
name: "Live",
type: "LiveStream",
target: scannerRef.current,
constraints: {
width: 640,
height: 480,
facingMode: "environment",
},
},
decoder: {
readers: [
"code_128_reader",
"ean_reader",
"ean_8_reader",
"code_39_reader",
"upc_reader",
],
},
locate: true,
};
// Inicia la cámara inmediatamente
codeReader.decodeFromVideoDevice(deviceId, videoRef.current!, (result) => {
if (result) onDetected(result.getText());
});
}).catch(console.error);
// El callback de error: tipamos `err` como `unknown`
const initCallback = (err: unknown) => {
if (err) {
console.error("Error al iniciar el escáner:", err);
return;
}
Quagga.start();
};
return () => { codeReader.reset(); };
}, [onDetected]);
Quagga.init(config as unknown as Record<string, unknown>, initCallback);
// Handler de detección: `data` es `unknown`, lo validamos
const handleDetected = (data: unknown) => {
// Validación segura
if (
data != null &&
typeof data === "object" &&
"codeResult" in data &&
data.codeResult != null &&
typeof data.codeResult === "object" &&
"code" in data.codeResult &&
typeof data.codeResult.code === "string"
) {
const code = data.codeResult.code.trim();
if (code !== "") {
onScan(code);
Quagga.stop(); // Opcional
}
}
};
Quagga.onDetected(handleDetected);
return () => {
Quagga.offDetected(handleDetected);
Quagga.stop();
};
}, [onScan]);
return (
<video
ref={videoRef}
<div
ref={scannerRef}
style={{
width: "100%",
maxWidth: "600px", // Más grande
border: "2px solid #004aad",
borderRadius: "10px"
maxWidth: "500px",
height: "300px",
borderRadius: "12px",
overflow: "hidden",
border: "2px solid #0056b3",
}}
autoPlay
muted
/>
);
}
}