diff --git a/public/file.svg b/public/file.svg new file mode 100644 index 0000000..004145c --- /dev/null +++ b/public/file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/globe.svg b/public/globe.svg new file mode 100644 index 0000000..567f17b --- /dev/null +++ b/public/globe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/next.svg b/public/next.svg new file mode 100644 index 0000000..5174b28 --- /dev/null +++ b/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/vercel.svg b/public/vercel.svg new file mode 100644 index 0000000..7705396 --- /dev/null +++ b/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/app/Editar/editar.css b/src/app/Editar/editar.css new file mode 100644 index 0000000..da41a86 --- /dev/null +++ b/src/app/Editar/editar.css @@ -0,0 +1,126 @@ + + +/* ====== FORMULARIO ====== */ +.editar { + max-width: 1000px; + display: flex; + gap: 20px; + margin: 40px auto; + flex-wrap: wrap; + justify-content: center; +} + +.columna { + display: flex; + flex-direction: column; + gap: 10px; +} + +label { + font-weight: bold; +} + +input { + padding: 6px 8px; + border: 1px solid #ccc; + border-radius: 5px; + outline: none; +} + +input:focus { + border-color: #003366; + box-shadow: 0 0 4px rgba(0, 51, 102, 0.3); +} + + +/* Botón cerrar sesión */ +.boton-cerrar { + background-color: #e60000; + color: white; + border: none; + padding: 8px 16px; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + transition: background-color 0.3s; + margin-top: 10px; +} + +.boton-cerrar:hover { + background-color: #cc0000; +} + +.titulo-editar { + position: absolute; + top: 50%; + left: 50%; /* centra horizontalmente */ + transform: translate(-50%, -50%); /* centra vertical y horizontalmente */ + + display: flex; + justify-content: space-between; + align-items: center; + width: 35%; /* ajusta el ancho del bloque de texto */ + font-size: 18px; + font-weight: bold; + color: #003366; + z-index: 5; +} + +.titulo-editar span { + flex: 1; + text-align: center; + cursor: pointer; + transition: color 0.3s, text-decoration 0.3s; +} + +/* Cambia de color al pasar el mouse */ +.titulo-editar span:hover { + color: #b8870b; +} + +.recuadro{ + box-shadow: 0 0 7px 0px black; + border-radius: 10px; /* Bordes redondeados */ + padding: 10px; /* Espacio interno */ + margin: 85px 350px; /* Separación del resto del contenido */ + background-color: #f9f9f9; /* Color de fondo */ +} + +.titulo-rec { + font-size: 20px; + font-weight: bold; + margin-left: 0; /* alinea el texto con el resto de la columna */ + text-align: left; /* asegura que esté alineado a la izquierda */ + margin-bottom: 8px; /* un poco de espacio debajo del título */ +} +.linea{ + border-bottom: 2px solid #003366; /* color y grosor de la línea */ + width: 100%; /* ocupa toda la columna */ + margin-bottom: 10px; /* espacio entre línea y los campos */ +} + +.botones { + display: flex; + justify-content: flex-end; /* Alinea los botones a la derecha */ + gap: 10px; /* Espacio entre ellos */ + margin-top: 15px; /* Separación del formulario */ +} +.guardar{ + background-color: #b8870b; + color: #003366; + border: none; + padding: 8px 16px; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + +} +.cancelar{ + background-color: #003366; + color: white; + border: none; + padding: 8px 16px; + border-radius: 4px; + cursor: pointer; + font-size: 14px; +} \ No newline at end of file diff --git a/src/app/Editar/page.tsx b/src/app/Editar/page.tsx index b672051..0e25a39 100644 --- a/src/app/Editar/page.tsx +++ b/src/app/Editar/page.tsx @@ -1,37 +1,68 @@ -import "../styles/layout/editar.scss"; -export default function Page() { +import './editar.css'; +export default function Home() { return ( -
-
- - - - - - - - -
+
+ -
- - - - - - - - -
-
- - - - - - +
+ + {/* Columna 1 */} +
+

CPU

+
+ + + + + + + + + + + + +
+ + {/* Columna 2 */} +
+

Inventario:

+
+ + + + + + + + + + + + +
+ + {/* Columna 3 */} +
+

Fecha Censo:

+
+ + + + + + + + + +
+ +
+ + +
- +
); } diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx new file mode 100644 index 0000000..a04e1f4 --- /dev/null +++ b/src/app/dashboard/page.tsx @@ -0,0 +1,91 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { useRouter } from "next/navigation"; +import BarcodeScanner from "../../components/BarcodeScanner"; +import "../styles/components/dashboard.scss"; + +type Equipo = { + id: string; + nombre: string; + estado: string; +}; + +const initialEquipos: Equipo[] = [ + { id: "001", nombre: "PC Aula 1", estado: "Disponible" }, + { id: "002", nombre: "PC Aula 2", estado: "En reparación" }, +]; + +export default function Dashboard() { + const router = useRouter(); + const [equipos, setEquipos] = useState(initialEquipos); + const [search, setSearch] = useState(""); + const [lastScan, setLastScan] = useState(""); + + useEffect(() => { + const loggedIn = localStorage.getItem("loggedIn"); + if (!loggedIn) router.push("/"); + }, [router]); + + const buscarEquipo = () => { + const resultado = equipos.filter( + e => e.id.includes(search) || e.nombre.toLowerCase().includes(search.toLowerCase()) + ); + setEquipos(resultado); + }; + + const cerrarSesion = () => { + localStorage.removeItem("loggedIn"); + router.push("/"); + }; + + const handleScan = (code: string) => { + setLastScan(code); + + const encontrado = equipos.find(e => e.id === code); + if (encontrado) { + alert(`Equipo encontrado: ${encontrado.nombre} (${encontrado.estado})`); + } else { + const nombre = prompt("Equipo no registrado. Ingresa el nombre:"); + if (nombre) { + setEquipos([...equipos, { id: code, nombre, estado: "Disponible" }]); + alert("Equipo agregado al inventario"); + } + } + }; + + return ( +
+
+

Dashboard Inventario Escolar

+ +
+ +
+

Buscar equipo

+
+ setSearch(e.target.value)} /> + +
+
+ +
+

Escanear código de barras

+ + {lastScan &&

Último código escaneado: {lastScan}

} +
+ +
+

Equipos

+
    + {equipos.map((e) => ( +
  • + {e.id} - {e.nombre} + {e.estado} +
  • + ))} +
+
+
+ ); +} diff --git a/src/app/reporte/page.tsx b/src/app/reporte/page.tsx new file mode 100644 index 0000000..6405220 --- /dev/null +++ b/src/app/reporte/page.tsx @@ -0,0 +1,88 @@ +import './reporte.css'; + +export default function Reporte() { + return ( +
+
+
+
+ +
+ +
+ Escanear código + Equipo + Reportes +
+ + +
+ +
+
+

REPORTE

+
+ +
+ + +
+
+
+ + +
+
+ {/* Columna 1 */} +
+

Filtro

+
+ + + + + + + + + + + + +
+
+ + +
+
+
+
+ ); +} \ No newline at end of file diff --git a/src/app/reporte/reporte.css b/src/app/reporte/reporte.css new file mode 100644 index 0000000..a276ed9 --- /dev/null +++ b/src/app/reporte/reporte.css @@ -0,0 +1,163 @@ +.pestaña { + position: absolute; + top: 50%; + left: 62%; + transform: translate(-50%, -50%); + + display: flex; + justify-content: space-around; /* espacio igual entre los tres textos */ + align-items: center; + width: 45%; /* controla el ancho total del bloque central */ + font-size: 18px; + color: #003366; + font-weight: bold; + z-index: 5; /* por encima del logo */ +} + +.pestaña span { + flex: 1; + text-align: center; + cursor: pointer; + transition: color 0.3s; +} + +.pestaña span:hover { + color: #b8870b; /* cambia de color al pasar el cursor */ +} + +.titulo-reporte{ + font-weight: bold; /* negrita */ + font-size: 24px; /* tamaño de texto */ + font-family: Arial, sans-serif; +} + +/*BUSCADOR*/ + +input[type="text"] { + width: 500px; /* más ancho, más largo */ + padding: 7px; /* mantiene espacio cómodo dentro */ + font-size: 18px; + border: 1px solid #ccc; + border-radius: 4px; +} + +button { + padding: 8px 12px; + font-size: 16px; + border: none; + background-color: #007bff; + color: white; + border-radius: 4px; + cursor: pointer; +} + +button:hover { + background-color: #0056b3; +} +.contenedor{ + margin-left: 80px; +} + +.filtro-rec{ + border: 2px solid #003366; /* Color del borde */ + border-radius: 10px; /* Bordes redondeados */ + padding: 10px; /* Espacio interno */ + margin: 80px 550px; /* Separación del resto del contenido */ + background-color: #f9f9f9; + position: relative; + left: 380px; + font-family: Arial, sans-serif; + top: -570px; /* sube 50px respecto a su posición original */ + + +} + +/* Estilos para los selects */ +select { + padding: 8px 12px; + border: 1px solid #003366; + border-radius: 5px; + background-color: white; + font-size: 14px; + width: 100%; + margin-bottom: 10px; +} + +select:focus { + border-color: #003366; + box-shadow: 0 0 4px rgba(0, 51, 102, 0.3); + outline: none; +} + +/* Botón de buscar */ +form[action="/buscar"] { + display: flex; + gap: 10px; + margin: 20px 0; + +} + +form[action="/buscar"] input { + padding: 8px 12px; + border: 1px solid #ccc; + border-radius: 5px; + width: 300px; +} + +form[action="/buscar"] button { + background-color: #003366; + color: white; + border: none; + padding: 8px 16px; + border-radius: 5px; + cursor: pointer; +} + +.botones-filtro { + display: flex; + justify-content: flex-end; /* botones a la derecha */ + gap: 10px; /* espacio entre botones */ + margin-top: 15px; /* separación del resto del formulario */ +} + +.aplicar { + background-color: #003366; + color: white; + border: none; + padding: 8px 16px; + border-radius: 4px; + cursor: pointer; + font-size: 14px; +} + +.limpiar { + background-color: #b8870b; + color: white; + border: none; + padding: 8px 16px; + border-radius: 4px; + cursor: pointer; + font-size: 14px; +} + +.tabla-rec{ + border: 2px solid #003366; /* Color del borde */ + border-radius: 10px; /* Bordes redondeados */ + padding: 10px; /* Espacio interno */ + margin: 50px 700px 0 80px; /* superior, derecha, inferior, izquierda */ + background-color: #f9f9f9; + font-family: Arial, sans-serif; + height: 400px; +} + +.boton-descargar{ + padding: 8px 12px; + font-size: 16px; + border: none; + background-color: #003366; + color: white; + border-radius: 4px; + cursor: pointer; + right: 30px; + margin-left: 700px; +} \ No newline at end of file diff --git a/src/app/styles/base/_globales.scss b/src/app/styles/base/_globales.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/styles/layout/__editar.scss b/src/app/styles/layout/__editar.scss new file mode 100644 index 0000000..ed6daec --- /dev/null +++ b/src/app/styles/layout/__editar.scss @@ -0,0 +1,11 @@ +.editar{ + + max-width: 1000px; + display: flex; + gap: 20px; +} + +.columna{ + display: flex ; + flex-direction: column; +} \ No newline at end of file diff --git a/src/app/styles/layout/card.scss b/src/app/styles/layout/card.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/styles/layout/dashboard.scss b/src/app/styles/layout/dashboard.scss new file mode 100644 index 0000000..86331ee --- /dev/null +++ b/src/app/styles/layout/dashboard.scss @@ -0,0 +1,83 @@ +.dashboard-container { + max-width: 900px; + margin: 50px auto; + padding: 20px; + background: #f8f9fa; + border-radius: 15px; + box-shadow: 0 4px 10px rgba(0,0,0,0.1); + font-family: Arial, sans-serif; +} + +h2 { + color: #004aad; // azul marino + font-size: 2rem; + margin-bottom: 15px; +} + +button { + background-color: #d4af37; // dorado + color: white; + border: none; + padding: 8px 16px; + border-radius: 8px; + cursor: pointer; + font-weight: bold; + transition: background 0.3s; +} + +button:hover { + background-color: #b5952e; +} + +input { + padding: 8px 12px; + border-radius: 8px; + border: 1px solid #ccc; + flex: 1; + font-size: 1rem; +} + +.search-section, .scanner-section, .equipos-section { + margin-top: 30px; +} + +.search-section input, .search-section button { + margin-right: 10px; +} + +.scanner-section video { + border: 2px solid #004aad; + border-radius: 10px; + max-width: 400px; + width: 100%; +} + +.equipos-section ul { + list-style: none; + padding: 0; +} + +.equipos-section li { + display: flex; + justify-content: space-between; + padding: 10px 15px; + border-radius: 8px; + border: 1px solid #ccc; + margin-bottom: 10px; + background: white; + transition: background 0.3s; +} + +.equipos-section li:hover { + background: #e6f0ff; +} + +.estado-disponible { + color: green; + font-weight: bold; +} + +.estado-reparacion { + color: red; + font-weight: bold; +} diff --git a/src/components/BarcodeScanner.tsx b/src/components/BarcodeScanner.tsx new file mode 100644 index 0000000..d324592 --- /dev/null +++ b/src/components/BarcodeScanner.tsx @@ -0,0 +1,42 @@ +"use client"; + +import { useEffect, useRef } from "react"; +import { BrowserMultiFormatReader } from "@zxing/library"; + +type Props = { onDetected: (code: string) => void; }; + +export default function BarcodeScanner({ onDetected }: Props) { + const videoRef = useRef(null); + + useEffect(() => { + const codeReader = new BrowserMultiFormatReader(); + if (!videoRef.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; + + // Inicia la cámara inmediatamente + codeReader.decodeFromVideoDevice(deviceId, videoRef.current!, (result) => { + if (result) onDetected(result.getText()); + }); + }).catch(console.error); + + return () => { codeReader.reset(); }; + }, [onDetected]); + + return ( +