diff --git a/app/(private)/AsignacionMesas/page.tsx b/app/(private)/AsignacionMesas/page.tsx index d9ce25e..9d87bb6 100644 --- a/app/(private)/AsignacionMesas/page.tsx +++ b/app/(private)/AsignacionMesas/page.tsx @@ -4,11 +4,9 @@ import Information from "@/app/Components/Global/Information/information"; import SearchUser from "@/app/Components/Global/SearchUser/searchUser"; import ShowError from "@/app/Components/Global/ShowError"; import Toggle from "@/app/Components/Global/Toggle/Toggle"; -import Table from "@/app/Components/Global/table"; import { envConfig } from "@/app/lib/config"; import { GetRegisterStudent } from "@/app/lib/getRegisterStudent"; -import axios from "axios"; async function getInscripcion(idCuenta: number) { try { @@ -23,44 +21,48 @@ async function getInscripcion(idCuenta: number) { } } -const headers = ["Inscrito", "Tiempo", "Confirmó"]; - export default async function Page(props: { searchParams?: Promise<{ key?: string; numAcount?: string; + table?:number }>; }) { const params = await props.searchParams; const key = params?.key; const numAcount = params?.numAcount ?? null; + const table = params?.table ?? null; + let student: any = null; + let platica: any = null; let inscripcion: any[] = []; let errorMessage = ""; if (numAcount) { const idCuenta = parseInt(numAcount); - const result = await axios.get(`${envConfig.apiUrl}/bitacora-mesa/cuenta/${idCuenta}`); - student = result.data + const result = await GetRegisterStudent(idCuenta); + if (result.error) { + errorMessage = `${result.error}`; + } else { + student = result[0]?.alumno as Student; + const rawPlatica = result[0]?.platica; + platica = + rawPlatica?.type === "Buffer" + ? Boolean(rawPlatica.data[0]) + : Boolean(rawPlatica); + console.log(result); + } const inscResult = await getInscripcion(idCuenta); if (!inscResult.error && Array.isArray(inscResult)) { inscripcion = inscResult; + } else { + inscripcion = []; } } - const tableData = Array.isArray(inscripcion) - ? inscripcion.map((ins) => ({ - Inscrito: ins.plataforma?.nombre || "—", - Tiempo: ins.tiempo_disponible - ? `${ins.tiempo_disponible} minutos` - : "—", - Confirmó: ins.platica?.data?.[0] === 1 ? "sí" : "no", - })) - : []; - return (
{errorMessage && } @@ -84,7 +86,6 @@ export default async function Page(props: { NoCuenta={student.id_cuenta} nombre={student.nombre} /> - , + content: , }, ]} /> diff --git a/app/(private)/Programas/page.tsx b/app/(private)/Programas/page.tsx index 078d3e4..43aba9e 100644 --- a/app/(private)/Programas/page.tsx +++ b/app/(private)/Programas/page.tsx @@ -1,41 +1,63 @@ "use client"; -import { useState } from "react"; + +import { useState, useEffect } from "react"; import styleprograms from "./programas.module.css"; +import { Interface } from "readline"; + + interface Programa { + id_programa: number; + programa: string; +} export default function Page() { const [modo, setModo] = useState("ver"); - const handleNuevo = () => { - setModo("nuevo"); - }; + const [programaSeleccionado, setProgramaSeleccionado] = useState(""); + const [programas, setProgramas] = useState([]); - const handleEditar = () => { - setModo("editar"); - }; + useEffect(() => { + fetch("http://localhost:5000/programa/") + .then((res) => res.json()) + .then((data) => setProgramas(data)) + .catch((error) => console.error("Error al cargar programas:", error)); + }, []); - const handleCancelar = () => { - setModo("ver"); - }; + const handleNuevo = () => setModo("nuevo"); + const handleEditar = () => setModo("editar"); + const handleCancelar = () => setModo("ver"); return (
+ + {} {modo === "ver" && (

PROGRAMAS

- + +

Programa

- setProgramaSeleccionado(e.target.value)} + > + + + {programas.map((programa) => ( + + ))} +
- + + + +
); diff --git a/app/Components/CheckBoxMesa.tsx b/app/Components/CheckBoxMesa.tsx index 1097315..b285af6 100644 --- a/app/Components/CheckBoxMesa.tsx +++ b/app/Components/CheckBoxMesa.tsx @@ -1,23 +1,123 @@ "use client"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import SearchUser from "./Global/SearchUser/searchUser"; -import SearchEquipo from "./SearchEquipo"; import Information from "./Global/Information/information"; +import SearchMesa from "./SearchMesa"; +import { getTableByCount } from "../lib/getTableByCount"; +import toast from "react-hot-toast"; +import axios from "axios"; +import { envConfig } from "../lib/config"; interface props { numAcount: string | null; - student: { - id_cuenta: number; - nombre: string; - carrera:{ - carrera:string; - } - }; + table: number | null; } -export default function CheckBoxMesa({ numAcount, student }: props) { - const [modo, setModo] = useState<"Mesa" | "Cuenta" | null>(null); +async function getEquipoId(idEquipo: number) { + try { + const res = await axios.get( + `${envConfig.apiUrl}/bitacora-mesa/table/${idEquipo}`, + ); + return res.data; + } catch (error: any) { + if (axios.isAxiosError(error)) { + return { + error: error.response?.data?.message || "Error al consultar equipo", + }; + } + + return { error: "Error desconocido" }; + } +} + +export default function CheckBoxMesa({ numAcount, table }: props) { + const [modo, setModo] = useState<"Mesa" | "Cuenta" | null>("Cuenta"); + const [bitacora, setBitacora] = useState(null); + const [tiempoRestante, setTiempoRestante] = useState(""); + const [minutos, setMinutos] = useState(); + + const fetchByCuenta = async (idCuenta: number) => { + const result = await getTableByCount(idCuenta); + + if (result?.error) { + toast.error(result.error); + setBitacora(null); + } else { + setBitacora(result); + } + }; + + const fetchByEquipo = async (idEquipo: number) => { + const result = await getEquipoId(idEquipo); + + if (result?.error) { + toast.error(result.error); + setBitacora(null); + } else { + setBitacora(result); + } + }; + + useEffect(() => { + if (!numAcount) return; + const idCuenta = parseInt(numAcount); + if (isNaN(idCuenta)) return; + + fetchByCuenta(idCuenta); + }, [numAcount]); + + useEffect(() => { + if (!table) return; + const idEquipo = table; + if (isNaN(idEquipo)) return; + + fetchByEquipo(idEquipo); + }, [table]); + + useEffect(() => { + if (!bitacora) return; + + const entrada = new Date(bitacora.tiempo_entrada).getTime(); + const asignadoMs = bitacora.tiempo_asignado * 60 * 1000; + + const interval = setInterval(() => { + const ahora = Date.now(); + const restante = entrada + asignadoMs - ahora; + + if (restante <= 0) { + setTiempoRestante("agotado"); + clearInterval(interval); + } else { + const minutos = Math.floor(restante / 60000); + setMinutos(minutos + 1); + const segundos = Math.floor((restante % 60000) / 1000); + setTiempoRestante(`${minutos} minutos con ${segundos} segundos`); + } + }, 1000); + + return () => clearInterval(interval); + }, [bitacora]); + + + const handleButton = async (e: React.MouseEvent) => { + e.preventDefault(); + + await axios.patch( + `${envConfig.apiUrl}/bitacora-mesa/cancelar/${bitacora.id_bitacora_mesa}`, + { tiempo_asignado: minutos }, + ); + + toast.success("Tiempo cancelado"); + + if (modo === "Cuenta" && numAcount) { + fetchByCuenta(parseInt(numAcount)); + } + + if (modo === "Mesa" && table) { + fetchByEquipo(table); + } + }; return ( <> @@ -46,10 +146,24 @@ export default function CheckBoxMesa({ numAcount, student }: props) { {modo === "Cuenta" && } - {modo === "Mesa" && } + {modo === "Mesa" && } - {student && ( - + {bitacora && ( + <> + + + )} ); diff --git a/app/Components/Global/SearchUser/searchUserTable.tsx b/app/Components/Global/SearchUser/searchUserTable.tsx new file mode 100644 index 0000000..2de3a56 --- /dev/null +++ b/app/Components/Global/SearchUser/searchUserTable.tsx @@ -0,0 +1,87 @@ +"use client"; + +import { useEffect, useState } from "react"; +import axios from "axios"; + +interface SearchUserProps { + value?: string | null; +} + +type Alumno = { + nombre: string; + carrera: string; + mesaActiva: boolean; +}; + +export default function SearchUserTable({ value }: SearchUserProps) { + const [numAcount, setNumAcount] = useState(value || ""); + const [alumno, setAlumno] = useState(null); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + // Si el prop cambia, actualizar el input + useEffect(() => { + if (value) setNumAcount(value); + }, [value]); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + if (!numAcount) { + setError("Ingresa un número de cuenta"); + return; + } + + setLoading(true); + setError(null); + setAlumno(null); + + try { + // Llamada a la API con Axios + const { data } = await axios.get(`/api/alumnos/${numAcount}`); + setAlumno(data); + } catch (err: any) { + setAlumno(null); + setError(err.response?.data?.message || "Alumno no encontrado"); + } finally { + setLoading(false); + } + }; + + return ( +
+
+ +
+ { + const value = e.target.value; + // Solo números y máximo 9 dígitos + if (/^\d*$/.test(value) && value.length <= 9) { + setNumAcount(value); + } + }} + placeholder="Coloca un número de cuenta..." + inputMode="numeric" + pattern="[0-9]*" + /> + +
+ + + {error &&

{error}

} + + {alumno && ( +
+

Nombre: {alumno.nombre}

+

Carrera: {alumno.carrera}

+

Mesa activada: {alumno.mesaActiva ? "Sí" : "No"}

+
+ )} +
+ ); +} diff --git a/app/Components/Mesas b/app/Components/Mesas new file mode 100644 index 0000000..c38d979 --- /dev/null +++ b/app/Components/Mesas @@ -0,0 +1,22 @@ +"use client"; +import axios from 'axios'; +import { useEffect } from 'react'; + +interface Props { + id_cuenta: number; +} + +export function Mesas({ id_cuenta }: Props) { + + useEffect(() => { + if (!id_cuenta) return; + + axios.get(`/cuenta/${id_cuenta}`) + .then(res => { + console.log('Mesas:', res.data); + }) + .catch(err => console.error(err)); + }, [id_cuenta]); + + return
Buscando mesas para cuenta {id_cuenta}...; +} diff --git a/app/Components/SearchMesa.tsx b/app/Components/SearchMesa.tsx new file mode 100644 index 0000000..ecda544 --- /dev/null +++ b/app/Components/SearchMesa.tsx @@ -0,0 +1,90 @@ +"use client"; +import axios from "axios"; +import { usePathname, useRouter, useSearchParams } from "next/navigation"; +import { useEffect, useState } from "react"; + +interface Mesa { + id_mesa: number; + nombre?: string; +} + +interface Props { + id_cuenta: number; +} + +function Mesas({ id_cuenta }: Props) { + const [mesas, setMesas] = useState([]); + const [loading, setLoading] = useState(false); + + useEffect(() => { + if (!id_cuenta) return; + + setLoading(true); + + axios + .get(`/cuenta/${id_cuenta}`) + .then((res) => { + setMesas(res.data); + }) + .catch((err) => console.error(err)) + .finally(() => setLoading(false)); + }, [id_cuenta]); + + if (loading) return

Cargando mesas...

; + + return ( +
+

Mesas asignadas

+ + {mesas.length === 0 ? ( +

No hay mesas

+ ) : ( +
    + {mesas.map((mesa) => ( +
  • + Mesa {mesa.id_mesa} {mesa.nombre && `- ${mesa.nombre}`} +
  • + ))} +
+ )} +
+ ); +} + +export default function SearchMesa() { + const [inputValue, setInputValue] = useState(""); + const [idCuenta, setIdCuenta] = useState(null); + const router = useRouter(); + const pathname = usePathname(); + const searchParams = useSearchParams(); + + const buscar = (e: React.FormEvent) => { + e.preventDefault(); + const params = new URLSearchParams(searchParams.toString()); + if (inputValue) { + params.set("table", `${inputValue}`); + router.push(`${pathname}?${params.toString()}`); + } + }; + + return ( + <> +
+ + +
+ setInputValue(e.target.value)} + /> + + +
+ + + ); +} diff --git a/app/lib/getTableByCount.ts b/app/lib/getTableByCount.ts new file mode 100644 index 0000000..debf82a --- /dev/null +++ b/app/lib/getTableByCount.ts @@ -0,0 +1,19 @@ +import axios from "axios"; +import { envConfig } from "./config"; + +export async function getTableByCount(idCuenta: number) { + try { + const res = await axios.get( + `${envConfig.apiUrl}/bitacora-mesa/cuenta/${idCuenta}`, + ); + return res.data; + } catch (error: any) { + if (axios.isAxiosError(error)) { + return { + error: error.response?.data?.message || "Error al consultar equipo", + }; + } + + return { error: "Error desconocido" }; + } +} \ No newline at end of file