This commit is contained in:
2026-01-29 12:35:43 -06:00
8 changed files with 515 additions and 87 deletions
+19 -18
View File
@@ -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 (
<section className="containerSection">
{errorMessage && <ShowError message={errorMessage} />}
@@ -84,7 +86,6 @@ export default async function Page(props: {
NoCuenta={student.id_cuenta}
nombre={student.nombre}
/>
<Table headers={headers} data={tableData} />
</div>
<AsignacionMesas
@@ -99,7 +100,7 @@ export default async function Page(props: {
{
key: "Liberar",
label: "Cancelar mesa",
content: <CheckBoxMesa numAcount={numAcount} student={student} />,
content: <CheckBoxMesa numAcount={numAcount} table={table} />,
},
]}
/>
+63 -29
View File
@@ -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<Programa[]>([]);
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 (
<section className="containerSection">
{}
{modo === "ver" && (
<section className="containersection">
<h3 className="title">PROGRAMAS</h3>
<label className="label">ID:2</label>
<label className="label">ID: 2</label>
<h2>Programa</h2>
<select>
<option value="0"></option>
<option value="1">ADOBE CREATIVE SUITE</option>
<option value="2">mmmmm</option>
<option value="3">mmmmm</option>
<option value="4">mmmmm</option>
<option value="5">mmmmm</option>
<select
value={programaSeleccionado}
onChange={(e) => setProgramaSeleccionado(e.target.value)}
>
<option value="">Selecciona un programa</option>
{programas.map((programa) => (
<option key={programa.id_programa} value={programa.id_programa}>
{programa.programa}
</option>
))}
</select>
<div className="margin">
<button className="button buttonSearch" onClick={handleEditar}>
<button
className="button buttonSearch"
onClick={handleEditar}
>
Editar
</button>
<button
className={`button buttonSearch ${styleprograms.button}`}
onClick={handleNuevo}
@@ -46,20 +68,28 @@ export default function Page() {
</section>
)}
{}
{modo === "editar" && (
<section className="containersection">
<h2 className="title">Editar Programa:</h2>
<label className="label">ID:2</label>
<select>
<option value="0">Elige</option>
<option value="1">ADOBE CREATIVE</option>
<option value="2">.....</option>
<option value="3">.....</option>
<option value="4">.....</option>
<option value="5">.....</option>
<h2 className="title">Editar Programa</h2>
<label className="label">ID: {programaSeleccionado}</label>
<select
value={programaSeleccionado}
onChange={(e) => setProgramaSeleccionado(e.target.value)}
>
<option value="">Elige un programa</option>
{programas.map((programa) => (
<option key={programa.id_programa} value={programa.id_programa}>
{programa.programa}
</option>
))}
</select>
<div className="margin">
<button className="button buttonSearch">Guardar</button>
<button
className={`button buttonCancel ${styleprograms.button}`}
onClick={handleCancelar}
@@ -70,13 +100,17 @@ export default function Page() {
</section>
)}
{}
{modo === "nuevo" && (
<section className="containersection">
<h2 className="title">Nuevo Programa</h2>
<label className="label">Programa</label>
<input type="text" placeholder="Coloca el programa " />
<input type="text" placeholder="Coloca el programa" />
<div className="margin">
<button className="button buttonSearch">Insertar</button>
<button
className={`button buttonCancel ${styleprograms.button}`}
onClick={handleCancelar}
+87 -26
View File
@@ -3,25 +3,45 @@
import { useEffect, useState } from "react";
import { envConfig } from "@/app/lib/config";
import toast from "react-hot-toast";
import { getEquipoByCount } from "../lib/getEquipoByCount";
type Props = {
idCuenta: number;
inscripcion: any[];
idCuenta: number;
};
export default function AsignacionMesas({
idCuenta,
inscripcion,
}: Props) {
export default function AsignacionMesas({ idCuenta, inscripcion }: Props) {
const [puedeContinuar, setPuedeContinuar] = useState(false);
const [mesas, setMesas] = useState<any[]>([]);
const [loadingMesas, setLoadingMesas] = useState(false);
const [mesaSeleccionada, setMesaSeleccionada] = useState<any>(null);
const [tiempo, setTiempo] = useState<number>(15);
const [bitacora, setBitacora] = useState<[] | null>(null);
const fetchByCuenta = async (idCuenta: number) => {
const result = await getEquipoByCount(idCuenta);
if (result?.error) {
setBitacora(null);
} else {
setBitacora(result);
toast.error("Ya cuentas con un equipo asignado");
}
};
useEffect(() => {
if (!idCuenta) return;
const Cuenta = idCuenta;
if (isNaN(Cuenta)) return;
fetchByCuenta(Cuenta);
}, [idCuenta]);
useEffect(() => {
if (!Array.isArray(inscripcion) || inscripcion.length === 0) return;
const todasAsistieron = inscripcion.every(
(ins) => ins.platica?.data?.[0] === 1
(ins) => ins.platica?.data?.[0] === 1,
);
if (!todasAsistieron) {
@@ -33,18 +53,17 @@ export default function AsignacionMesas({
setPuedeContinuar(true);
}, [inscripcion]);
useEffect(() => {
console.log(envConfig.apiUrl);
console.log(puedeContinuar, idCuenta);
if (!puedeContinuar || !idCuenta) return;
const fetchMesas = async () => {
try {
setLoadingMesas(true);
const res = await fetch(
`${envConfig.apiUrl}/mesa/activo`,
{ cache: "no-store" }
);
const res = await fetch(`${envConfig.apiUrl}/mesa/activo`, {
cache: "no-store",
});
if (!res.ok) {
throw new Error("No se pudieron cargar las mesas");
@@ -62,37 +81,77 @@ export default function AsignacionMesas({
fetchMesas();
}, [puedeContinuar, idCuenta]);
const submit = async (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
if (!mesaSeleccionada) {
toast.error("Selecciona un mesa");
return;
}
try {
const body = {
tiempo_asignado: tiempo,
id_mesa: Number(mesaSeleccionada),
id_alumno_inscrito: inscripcion[0].id_alumno_inscrito,
};
const res = await fetch(`${envConfig.apiUrl}/bitacora-mesa`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
if (!res.ok) {
throw new Error("Error al asignar mesa");
}
setBitacora([]);
toast.success("Mesa asignada correctamente");
} catch (error) {
toast.error("No se pudo asignar la mesa");
}
};
if (!puedeContinuar) return null;
if (bitacora) {
return <></>;
}
return (
<div className="containerForm">
<label style={{ marginTop: "1rem" }}>
Seleccionar tiempo
</label>
<label style={{ marginTop: "1rem" }}>Seleccionar tiempo</label>
<select>
<option>15 minutos</option>
<option>30 minutos</option>
<option>40 minutos</option>
<option>60 minutos</option>
<select
value={tiempo}
onChange={(e) => setTiempo(Number(e.target.value))}
>
<option value={15}>15 minutos</option>
<option value={30}>30 minutos</option>
<option value={40}>40 minutos</option>
<option value={60}>60 minutos</option>
</select>
<label style={{ marginTop: "1rem" }}>
Seleccione una mesa
</label>
<label style={{ marginTop: "1rem" }}>Seleccione una mesa</label>
<div className="groupInput">
<select disabled={loadingMesas || mesas.length === 0}>
<select
disabled={loadingMesas || mesas.length === 0}
onChange={(e) => setMesaSeleccionada(e.target.value)}
>
{loadingMesas && <option>Cargando mesas...</option>}
{!loadingMesas && mesas.length === 0 && (
<option>No hay mesas disponibles</option>
)}
<option value={0}>Selecciona una mesa</option>
{!loadingMesas &&
mesas.map((mesa) => (
<option key={mesa.idMesa} value={mesa.idMesa}>
Mesa {mesa.idMesa}
<option key={mesa.id_mesa} value={mesa.id_mesa}>
Mesa {mesa.id_mesa}
</option>
))}
</select>
@@ -100,9 +159,11 @@ export default function AsignacionMesas({
<button
className="button buttonSearch"
disabled={loadingMesas || mesas.length === 0}
onClick={submit}
>
Asignar Mesa
</button>
</div>
</div>
);
+128 -14
View File
@@ -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<any>(null);
const [tiempoRestante, setTiempoRestante] = useState<string>("");
const [minutos, setMinutos] = useState<number>();
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<HTMLButtonElement>) => {
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) {
</div>
{modo === "Cuenta" && <SearchUser value={numAcount ?? null} />}
{modo === "Mesa" && <SearchEquipo value={ null}/>}
{modo === "Mesa" && <SearchMesa />}
{student && (
<Information NoCuenta={student.id_cuenta} nombre={student.nombre} carrera={student.carrera?.carrera}/>
{bitacora && (
<>
<Information
NoCuenta={bitacora.alumno_inscrito.alumno.id_cuenta}
Nombre={bitacora.alumno_inscrito.alumno.nombre}
Tiempo={tiempoRestante}
Mesa={bitacora.mesa.id_mesa}
/>
<button
className="button buttonSearch"
style={{ marginTop: "1rem" }}
onClick={handleButton}
>
Cancelar tiempo
</button>
</>
)}
</>
);
@@ -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<Alumno | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
// Si el prop cambia, actualizar el input
useEffect(() => {
if (value) setNumAcount(value);
}, [value]);
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
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<Alumno>(`/api/alumnos/${numAcount}`);
setAlumno(data);
} catch (err: any) {
setAlumno(null);
setError(err.response?.data?.message || "Alumno no encontrado");
} finally {
setLoading(false);
}
};
return (
<div className="search-user">
<form className="containerForm" onSubmit={handleSubmit}>
<label className="label">No. Cuenta</label>
<div className="groupInput">
<input
type="text"
value={numAcount}
onChange={(e) => {
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]*"
/>
<button className="button buttonSearch" type="submit" disabled={loading}>
{loading ? "Buscando..." : "Buscar"}
</button>
</div>
</form>
{error && <p style={{ color: "red", marginTop: "10px" }}>{error}</p>}
{alumno && (
<div className="alumno-info" style={{ marginTop: "15px" }}>
<p><strong>Nombre:</strong> {alumno.nombre}</p>
<p><strong>Carrera:</strong> {alumno.carrera}</p>
<p><strong>Mesa activada:</strong> {alumno.mesaActiva ? "Sí" : "No"}</p>
</div>
)}
</div>
);
}
+22
View File
@@ -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 <div> </div>Buscando mesas para cuenta {id_cuenta}...</div>;
}
+90
View File
@@ -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<Mesa[]>([]);
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 <p>Cargando mesas...</p>;
return (
<div>
<h3>Mesas asignadas</h3>
{mesas.length === 0 ? (
<p>No hay mesas</p>
) : (
<ul>
{mesas.map((mesa) => (
<li key={mesa.id_mesa}>
Mesa {mesa.id_mesa} {mesa.nombre && `- ${mesa.nombre}`}
</li>
))}
</ul>
)}
</div>
);
}
export default function SearchMesa() {
const [inputValue, setInputValue] = useState("");
const [idCuenta, setIdCuenta] = useState<number | null>(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 (
<>
<form className="containerForm" onSubmit={buscar}>
<label>Numero de mesa</label>
<div className="groupInput">
<input
type="number"
placeholder="Coloca el numero de mesa"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<button type="submit" className="button buttonSearch">
Buscar
</button>
</div>
</form>
</>
);
}
+19
View File
@@ -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" };
}
}