diff --git a/app/(private)/AgregarTiempo/Addtime.tsx b/app/(private)/AgregarTiempo/Addtime.tsx index e4b6aa0..3b1b410 100644 --- a/app/(private)/AgregarTiempo/Addtime.tsx +++ b/app/(private)/AgregarTiempo/Addtime.tsx @@ -10,6 +10,7 @@ import "./addTime.css"; import SelectionCo from "@/app/Components/Selection/SelectionCo"; import axios from "axios"; import { envConfig } from "@/app/lib/config"; +import Cookies from "js-cookie"; const PLATAFORMA_MAP: Record = { WINDOWS: 1, @@ -28,17 +29,15 @@ export default function AddTime({ }: AddTimeProps) { const router = useRouter(); - // 🔹 selección plataforma const [plataformaSeleccionada, setPlataformaSeleccionada] = useState< string | null >(null); - // 🔹 datos del recibo const [folio, setFolio] = useState(""); const [amount, setAmount] = useState(""); - const [date, setDate] = useState(""); - - // 🔹 restricción de fechas + const todayISO = new Date().toISOString().split("T")[0]; + const [date, setDate] = useState(todayISO); + const day = new Date(); const year = day.getFullYear(); const month = day.getMonth(); @@ -47,7 +46,6 @@ export default function AddTime({ const minFecha = new Date(year, month, 1).toISOString().split("T")[0]; const maxFecha = new Date(year, month, today).toISOString().split("T")[0]; - // 🔹 lógica central (AQUÍ crecerá después) const handleSaveReceipt = async () => { if (!numAcount) { toast.error("Busca de nuevo al estudiante"); @@ -77,23 +75,27 @@ export default function AddTime({ const id_plataforma = PLATAFORMA_MAP[plataformaSeleccionada]; try { - await PostReceipt({ - id_cuenta: numAcount, - folio_recibo: folio, - monto: Number(amount), - fecha_recibo: date, - }); + const token = Cookies.get("token"); + const headers = { Authorization: `Bearer ${token}` }; - await axios.post(`${envConfig.apiUrl}/alumno-inscrito/tiempo`, { - idCuenta: numAcount, - idPlataforma: id_plataforma, - }); + await axios.post( + `${envConfig.apiUrl}/operations/time`, + { + monto: Number(amount), + id_cuenta: numAcount, + idPlataforma: id_plataforma, + folio_recibo: folio, + fecha_recibo: date, + }, + { headers }, + ); toast.success("Tiempo Guardado"); setFolio(""); setAmount(""); setDate(""); + router.refresh(); } catch (err: any) { toast.error(err.message || "Error al guardar"); } @@ -171,3 +173,4 @@ export default function AddTime({ ); } +//IO \ No newline at end of file diff --git a/app/(private)/AgregarTiempo/addTime.css b/app/(private)/AgregarTiempo/addTime.css index 1fe80b1..1a94679 100644 --- a/app/(private)/AgregarTiempo/addTime.css +++ b/app/(private)/AgregarTiempo/addTime.css @@ -9,3 +9,24 @@ flex-direction: column; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); } + +.firstPartInformationTime { + display: flex; + flex-direction: column; + width: 100%; + max-width: 450px; +} + +.containeInformationTime { + display: flex; + gap: 10px; + width: 100%; +} + +@media (max-width: 800px) { + .containeInformationTime { + flex-direction: column; + justify-content: center; + align-items: center; + } +} \ No newline at end of file diff --git a/app/(private)/AgregarTiempo/page.tsx b/app/(private)/AgregarTiempo/page.tsx index f8cc99c..c8204d3 100644 --- a/app/(private)/AgregarTiempo/page.tsx +++ b/app/(private)/AgregarTiempo/page.tsx @@ -3,25 +3,12 @@ import Information from "@/app/Components/Global/Information/information"; import ShowError from "@/app/Components/Global/ShowError"; import { GetRegisterStudent } from "@/app/lib/getRegisterStudent"; -import { envConfig } from "@/app/lib/config"; import "./addTime.css"; import AddTime from "./Addtime"; +import Table from "@/app/Components/Global/table"; -async function getInscripcion(idCuenta: number) { - try { - const res = await fetch(`${envConfig.apiUrl}/alumno-inscrito/${idCuenta}`, { - headers: { "Content-Type": "application/json" }, - cache: "no-store", - }); - - if (!res.ok) throw new Error("No se pudo cargar inscripción"); - - return await res.json(); - } catch { - return []; - } -} +const headers = ["Inscrito", "Tiempo", "Confirmó"]; export default async function Page(props: { searchParams?: Promise<{ numAcount: string }>; @@ -41,13 +28,21 @@ export default async function Page(props: { errorMessage = result.error; } else { student = result[0]?.alumno; - inscripcion = await getInscripcion(idCuenta); + inscripcion = result; } } - const plataformasInscritas = inscripcion.map( - (ins) => ins.plataforma?.nombre - ); + 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", + })) + : []; + + const plataformasInscritas = inscripcion.map((ins) => ins.plataforma?.nombre); return (
@@ -55,22 +50,27 @@ export default async function Page(props: {

AGREGAR TIEMPO

- +
+
+ + {student && ( + <> + - {student && ( - <> - + + + )} +
- - - )} + {student && } + ); } -//IO \ No newline at end of file +//IO diff --git a/app/(private)/Alta/page.tsx b/app/(private)/Alta/page.tsx index a90117a..0ad2646 100644 --- a/app/(private)/Alta/page.tsx +++ b/app/(private)/Alta/page.tsx @@ -2,13 +2,19 @@ import RegisterAlta from "@/app/Components/Alta/registerAlta"; import "./style.css"; -export default async function Page(props: { searchParams?: Promise<{}> }) { +export default async function Page(props: { + searchParams?: Promise<{ + numAcount: string; + }>; +}) { const params = await props.searchParams; + const numAcount = params?.numAcount ? params.numAcount : null; + return (

ALTA USUARIO

- +
); } diff --git a/app/(private)/AsignacionEquipo/PlaticaGate.tsx b/app/(private)/AsignacionEquipo/PlaticaGate.tsx index e9ea405..c606469 100644 --- a/app/(private)/AsignacionEquipo/PlaticaGate.tsx +++ b/app/(private)/AsignacionEquipo/PlaticaGate.tsx @@ -4,21 +4,48 @@ import { useEffect, useState } from "react"; import { envConfig } from "@/app/lib/config"; import toast from "react-hot-toast"; +import { getEquipoByCount } from "@/app/lib/getEquipoByCount"; + +import "./asignacion.css"; + type Props = { inscripcion: any[]; - idCuenta: number; + numAcount: number; }; -export default function PlaticaGate({ inscripcion, idCuenta }: Props) { +export default function PlaticaGate({ inscripcion, numAcount }: Props) { const [puedeContinuar, setPuedeContinuar] = useState(false); const [equipos, setEquipos] = useState([]); const [loadingEquipos, setLoadingEquipos] = useState(false); + const [equipoSeleccionado, setEquipoSeleccionado] = useState(null); + const [open, setOpen] = useState(false); + const [tiempo, setTiempo] = useState(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 (!numAcount) return; + const idCuenta = numAcount; + if (isNaN(idCuenta)) return; + + fetchByCuenta(idCuenta); + }, [numAcount]); 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) { @@ -31,17 +58,17 @@ export default function PlaticaGate({ inscripcion, idCuenta }: Props) { }, [inscripcion]); useEffect(() => { - if (!puedeContinuar || !idCuenta) return; + if (!puedeContinuar || !numAcount) return; const fetchEquipos = async () => { try { setLoadingEquipos(true); const res = await fetch( - `${envConfig.apiUrl}/equipo/student/${idCuenta}`, + `${envConfig.apiUrl}/equipo/student/${numAcount}`, { cache: "no-store", - } + }, ); if (!res.ok) { @@ -58,50 +85,120 @@ export default function PlaticaGate({ inscripcion, idCuenta }: Props) { }; fetchEquipos(); - }, [puedeContinuar, idCuenta]); + }, [puedeContinuar, numAcount]); if (!puedeContinuar) return null; + const submit = async (e: React.MouseEvent) => { + e.preventDefault(); + + if (!equipoSeleccionado) { + toast.error("Selecciona un equipo"); + return; + } + + try { + const body = { + tiempo_asignado: tiempo, + ubicacion: equipoSeleccionado.ubicacion ?? null, + id_equipo: equipoSeleccionado.id_equipo, + id_alumno_inscrito: inscripcion[0].id_alumno_inscrito, + }; + + const res = await fetch(`${envConfig.apiUrl}/bitacora`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(body), + }); + + if (!res.ok) { + throw new Error("Error al asignar equipo"); + } + + setBitacora([]) + toast.success("Equipo asignado correctamente"); + } catch (error) { + toast.error("No se pudo asignar el equipo"); + } + }; + + if (bitacora) { + return <>; + } + return (
- + - setTiempo(Number(e.target.value))} + > + + + + + - +
- + {open && ( +
+ {equipos.length === 0 && ( +
+ No hay equipos disponibles +
+ )} + + {equipos.map((eq) => ( +
{ + setEquipoSeleccionado(eq); + setOpen(false); + }} + > + {eq.ubicacion} {eq.nombre_equipo} +
+ ))} +
+ )} +
diff --git a/app/(private)/AsignacionEquipo/asignacion.css b/app/(private)/AsignacionEquipo/asignacion.css new file mode 100644 index 0000000..997a905 --- /dev/null +++ b/app/(private)/AsignacionEquipo/asignacion.css @@ -0,0 +1,93 @@ +.customSelect { + border: 1px solid #ccc; + border-radius: 6px; + position: relative; + flex: 1; + width: 100%; + font-size: 14px; +} + +.customSelect.disabled { + opacity: 0.6; + pointer-events: none; +} + +.option { + padding: 10px 12px; + cursor: pointer; + display: flex; + align-items: center; + gap: 8px; + background: #fff; +} + +.option:not(.disabled):hover { + background-color: #f0f0f0; +} + +.option.disabled { + cursor: not-allowed; + color: #999; +} + +.option.active { + background-color: #e6f0ff; + font-weight: 600; +} + +.option:hover { + background-color: #f0f0f0; +} + +/* ICONOS */ +.WINDOWS::before { + content: ""; + width: 20px; + height: 20px; + background: url("/windows.png") no-repeat center / contain; +} + +.MACINTOSH::before { + content: ""; + width: 20px; + height: 20px; + background: url("/apple.png") no-repeat center / contain; +} + +.PROFESORES::before { + content: ""; + width: 20px; + height: 20px; + background: url("/teacher.png") no-repeat center / contain; +} + +.selectHeader { + border-radius: 6px; + padding: 8px 12px; + display: flex; + justify-content: space-between; + align-items: center; + cursor: pointer; + background: #fff; +} + +.placeholder { + color: #999; +} + +.arrow { + font-size: 12px; +} + +.options { + position: absolute; + bottom: 105%; + left: 0; + right: 0; + border: 1px solid #ccc; + border-radius: 6px; + background: #fff; + z-index: 10; + max-height: 250px; + overflow-y: auto; +} \ No newline at end of file diff --git a/app/(private)/AsignacionEquipo/page.tsx b/app/(private)/AsignacionEquipo/page.tsx index cc1fe9f..e7975ae 100644 --- a/app/(private)/AsignacionEquipo/page.tsx +++ b/app/(private)/AsignacionEquipo/page.tsx @@ -112,7 +112,7 @@ export default async function Page(props: { )} @@ -124,7 +124,7 @@ export default async function Page(props: { label: "Cancelar tiempo", content: ( <> - + ), }, diff --git a/app/(private)/AsignacionMesas/page.tsx b/app/(private)/AsignacionMesas/page.tsx index b30e5ff..f0203de 100644 --- a/app/(private)/AsignacionMesas/page.tsx +++ b/app/(private)/AsignacionMesas/page.tsx @@ -7,13 +7,13 @@ import Toggle from "@/app/Components/Global/Toggle/Toggle"; import { envConfig } from "@/app/lib/config"; import { GetRegisterStudent } from "@/app/lib/getRegisterStudent"; +import axios from "axios"; async function getInscripcion(idCuenta: number) { try { - const res = await fetch( - `${envConfig.apiUrl}/alumno-inscrito/${idCuenta}`, - { cache: "no-store" } - ); + const res = await fetch(`${envConfig.apiUrl}/alumno-inscrito/${idCuenta}`, { + cache: "no-store", + }); if (!res.ok) throw new Error("Error al cargar inscripción"); return await res.json(); @@ -39,13 +39,8 @@ export default async function Page(props: { if (numAcount) { const idCuenta = parseInt(numAcount); - const result = await GetRegisterStudent(idCuenta); - - if (result.error) { - errorMessage = result.error; - } else { - student = result[0]?.alumno; - } + const result = await axios.get(`${envConfig.apiUrl}/bitacora-mesa/cuenta/${idCuenta}`); + student = result.data const inscResult = await getInscripcion(idCuenta); if (!inscResult.error && Array.isArray(inscResult)) { @@ -101,8 +96,8 @@ export default async function Page(props: { }, { key: "Liberar", - label: "Liberar mesa", - content: , + label: "Cancelar mesa", + content: , }, ]} /> diff --git a/app/(private)/BitacoraSanciones/page.tsx b/app/(private)/BitacoraSanciones/page.tsx index 78cb26d..56f4cc9 100644 --- a/app/(private)/BitacoraSanciones/page.tsx +++ b/app/(private)/BitacoraSanciones/page.tsx @@ -2,9 +2,11 @@ import BitacoraAlumno from "@/app/Components/BitacoraSanciones/BitacoraAlumno"; import BitacoraEquipo from "@/app/Components/BitacoraSanciones/BitacoraEquipo"; import BitacoraMesas from "@/app/Components/BitacoraSanciones/BitacoraMesas"; import Sanciones from "@/app/Components/BitacoraSanciones/Sanciones"; +import SearchUser from "@/app/Components/Global/SearchUser/searchUser"; import SearchUserWithDate from "@/app/Components/Global/SearchUser/SearchUserWithDate"; import ShowError from "@/app/Components/Global/ShowError"; import Toggle from "@/app/Components/Global/Toggle/Toggle"; +import QuitarSancion from "@/app/Components/QuitarSancion/QuitarSancion"; import { GetStudent } from "@/app/lib/getStudent"; import { GetSancionByStudent } from "@/app/lib/getStudent copy"; @@ -42,7 +44,7 @@ export default async function Page(props: { options={[ { key: "Equipo", - label: "Bitacora equipo", + label: "Bitácora equipo", content: ( <> @@ -51,7 +53,7 @@ export default async function Page(props: { }, { key: "Alumno", - label: "Bitacora alumno", + label: "Bitácora alumno", content: ( <> @@ -61,7 +63,7 @@ export default async function Page(props: { }, { key: "Mesas", - label: "Bitacora mesas", + label: "Bitácora mesas", content: ( <> @@ -77,6 +79,18 @@ export default async function Page(props: { ), }, + { + key: "EliminarSanciones", + label: "Eliminar Sanciones", + content: ( + <> +
+ + {/* */} +
+ + ), + }, ]} /> diff --git a/app/(private)/Impresiones/page.tsx b/app/(private)/Impresiones/page.tsx index 48e43df..3be6b65 100644 --- a/app/(private)/Impresiones/page.tsx +++ b/app/(private)/Impresiones/page.tsx @@ -64,6 +64,7 @@ export default async function Page(props: { key={1} costs={[{ value: 1 }, { value: 2 }]} numAcount={student.id_cuenta} + id_servicio={1} /> ), }, @@ -84,6 +85,7 @@ export default async function Page(props: { { value: 14 }, ]} numAcount={student.id_cuenta} + id_servicio={2} /> ), }, @@ -113,6 +115,7 @@ export default async function Page(props: { { value: 200 }, ]} numAcount={student.id_cuenta} + id_servicio={3} /> ), }, @@ -124,6 +127,7 @@ export default async function Page(props: { key={4} costs={[{ value: 1 }, { value: 2 }, { value: 5 }]} numAcount={student.id_cuenta} + id_servicio={6} /> ), }, diff --git a/app/(private)/InformacionEquipo/InformacionEquipo.tsx b/app/(private)/InformacionEquipo/InformacionEquipo.tsx index e9524d9..f8cf187 100644 --- a/app/(private)/InformacionEquipo/InformacionEquipo.tsx +++ b/app/(private)/InformacionEquipo/InformacionEquipo.tsx @@ -16,6 +16,7 @@ export default function InformacionEquipo({ defaultKey?: string; }) { const [id, setId] = useState(null); + const [ubicacion, setUbicacion] = useState(null); return (
@@ -36,10 +37,10 @@ export default function InformacionEquipo({ <> { - setId(sala); + setUbicacion(sala); }} /> - , + , ), }, @@ -53,7 +54,7 @@ export default function InformacionEquipo({ setId(sala); }} /> - + ), }, diff --git a/app/(private)/InformacionEquipo/informacionequipo.css b/app/(private)/InformacionEquipo/informacionequipo.css index 151831f..0ec5f3c 100644 --- a/app/(private)/InformacionEquipo/informacionequipo.css +++ b/app/(private)/InformacionEquipo/informacionequipo.css @@ -12,11 +12,15 @@ flex-direction: column; align-items: start; } + +.form-container { + gap: 10px; +} + .form-container label { font-size: 14px; - margin-right: 10px; /* espacio entre "Salas" y la caja */ - display: inline-block; /* lo hace estar en la misma línea */ - vertical-align: middle; /* alinea verticalmente con el select */ + display: inline-block; + vertical-align: middle; } .form-container select { @@ -25,8 +29,9 @@ border: 1px solid #ccc; border-radius: 6px; outline: none; - vertical-align: middle; /* asegura alineación */ + vertical-align: middle; transition: all 0.2s ease-in-out; + margin-bottom: 1rem; } .form-container select:hover { @@ -43,9 +48,9 @@ .checkbox-grid { display: grid; grid-template-columns: repeat(3, 240px); /* ancho fijo por columna */ - gap: 6px 20px; + gap: 10px 20px; flex-wrap: wrap; - margin-right: 2px; + justify-content: start; } .checkbox-grid label { @@ -54,6 +59,7 @@ font-size: 13px; cursor: pointer; white-space: nowrap; + width: fit-content; } .checkbox-grid input[type="checkbox"] { diff --git a/app/(private)/Monitor/MachineTable.tsx b/app/(private)/Monitor/MachineTable.tsx index 155e73a..fb4518a 100644 --- a/app/(private)/Monitor/MachineTable.tsx +++ b/app/(private)/Monitor/MachineTable.tsx @@ -3,48 +3,61 @@ import { useEffect, useState } from "react"; import styles from "./Page.module.css"; +type AreaUbicacion = { + id_area_ubicacion: number; + area: string; +}; + type Equipo = { id_equipo: number; nombre_equipo: string; ubicacion: string; - activo: { - data: number[]; - }; - plataforma: { - nombre: string; - }; - areaUbicacion: { - area: string; - }; + activo: { data: number[] }; + plataforma: { nombre: string }; + areaUbicacion: { area: string }; }; export default function MachineTable() { const [machines, setMachines] = useState([]); + const [areas, setAreas] = useState([]); + const [selectedArea, setSelectedArea] = useState("Todo"); const [loading, setLoading] = useState(true); const fetchMachines = async () => { - try { - const res = await fetch( - `${process.env.NEXT_PUBLIC_API_URL}/equipo`, - { cache: "no-store" } - ); - const data = await res.json(); - setMachines(data); - } catch (error) { - console.error("Error al cargar equipos", error); - } finally { - setLoading(false); - } + const res = await fetch( + `${process.env.NEXT_PUBLIC_API_URL}/equipo/disable`, + { cache: "no-store" }, + ); + setMachines(await res.json()); + setLoading(false); + }; + + const fetchAreas = async () => { + const res = await fetch( + `${process.env.NEXT_PUBLIC_API_URL}/area-ubicacion`, + { cache: "no-store" }, + ); + setAreas(await res.json()); }; useEffect(() => { fetchMachines(); + fetchAreas(); }, []); if (loading) { return

Cargando equipos...

; } + const filteredMachines = + selectedArea === "Todo" + ? machines + : machines.filter( + (machine) => machine.areaUbicacion.area === selectedArea, + ); + + if (loading) return

Cargando equipos...

; + return (
@@ -56,25 +69,40 @@ export default function MachineTable() { + + + - {machines.map((machine) => { - const disponible = machine.activo.data[0] === 1; - - return ( - - - - - - - - ); - })} + {filteredMachines.map((machine) => ( + + + + + + + + ))}
Área Disponible
+ + + + + +
{machine.ubicacion}{machine.nombre_equipo}{machine.plataforma.nombre}{machine.areaUbicacion.area} - {disponible ? "Sí" : "No"} -
{machine.ubicacion}{machine.nombre_equipo} + {machine.plataforma.nombre} + {machine.areaUbicacion.area}{machine.activo.data[0] === 1 ? "Sí" : "No"}
diff --git a/app/(private)/Monitor/MachineTableActive.tsx b/app/(private)/Monitor/MachineTableActive.tsx new file mode 100644 index 0000000..b8990ba --- /dev/null +++ b/app/(private)/Monitor/MachineTableActive.tsx @@ -0,0 +1,107 @@ +"use client"; + +import { useEffect, useState } from "react"; +import styles from "./Page.module.css"; + +type AreaUbicacion = { + id_area_ubicacion: number; + area: string; +}; + +type Equipo = { + id_equipo: number; + nombre_equipo: string; + ubicacion: string; + plataforma: string; + area: string; + ocupado:boolean; +}; + + +export default function MachineTableActive() { + const [machines, setMachines] = useState([]); + const [areas, setAreas] = useState([]); + const [selectedArea, setSelectedArea] = useState("Todo"); + const [loading, setLoading] = useState(true); + + const fetchMachines = async () => { + const res = await fetch( + `${process.env.NEXT_PUBLIC_API_URL}/equipo/active`, + { cache: "no-store" }, + ); + setMachines(await res.json()); + setLoading(false); + }; + + const fetchAreas = async () => { + const res = await fetch( + `${process.env.NEXT_PUBLIC_API_URL}/area-ubicacion`, + { cache: "no-store" }, + ); + setAreas(await res.json()); + }; + + useEffect(() => { + fetchMachines(); + fetchAreas(); + }, []); + + const filteredMachines = + selectedArea === "Todo" + ? machines + : machines.filter( + (machine) => machine.area === selectedArea, + ); + + if (loading) return

Cargando equipos...

; + + return ( +
+ + + + + + + + + + + + + + + + {filteredMachines.map((machine) => ( + + + + + + + + ))} + +
UbicaciónNombre EquipoPlataformaÁreaDisponible
+ + + + + +
{machine.ubicacion}{machine.nombre_equipo} + {machine.plataforma} + {machine.area}{!machine.ocupado ? "Sí" : "No"}
+
+ ); +} diff --git a/app/(private)/Monitor/Page.module.css b/app/(private)/Monitor/Page.module.css index 7ea84a2..29c8352 100644 --- a/app/(private)/Monitor/Page.module.css +++ b/app/(private)/Monitor/Page.module.css @@ -17,6 +17,7 @@ } .ocupado { + background-color: rgb(173, 233, 253); color: red; font-weight: bold; } @@ -39,3 +40,39 @@ .resetButton:hover { background-color: #cc0000; } + +.WINDOWS::before { + content: ""; + display: inline-block; + width: 20px; + height: 20px; + background-image: url("/windows.png"); + background-size: contain; + background-repeat: no-repeat; + margin-right: 6px; + vertical-align: middle; +} + +.MACINTOSH::before { + content: ""; + display: inline-block; + width: 20px; + height: 20px; + background-image: url("/apple.png"); + background-size: contain; + background-repeat: no-repeat; + margin-right: 6px; + vertical-align: middle; +} + +.PROFESORES::before { + content: ""; + display: inline-block; + width: 20px; + height: 20px; + background-image: url("/teacher.png"); + background-size: contain; + background-repeat: no-repeat; + margin-right: 6px; + vertical-align: middle; +} \ No newline at end of file diff --git a/app/(private)/Monitor/page.tsx b/app/(private)/Monitor/page.tsx index f81c840..b05be2e 100644 --- a/app/(private)/Monitor/page.tsx +++ b/app/(private)/Monitor/page.tsx @@ -1,23 +1,46 @@ +import Toggle from "@/app/Components/Global/Toggle/Toggle"; import MachineTable from "./MachineTable"; import styles from "./Page.module.css"; +import ToggleTable from "@/app/Components/Global/Toggle/ToggleTable"; +import MachineTableActive from "./MachineTableActive"; export default async function Page(props: { - searchParams?: Promise<{ numAcount: string }>; + searchParams?: Promise<{ numAcount: string; key: string }>; }) { const params = await props.searchParams; const numAcount = params?.numAcount ?? null; + const key = params?.key ?? "active"; return (
-

MONITOR DE MÁQUINAS DISPONIBLES

+

MONITOR DE MÁQUINAS

- +
- - + + + + ), + }, + { + key: "disable", + label: "No Disponibles", + content: ( + <> + + + ), + }, + ]} + />
); } diff --git a/app/(private)/QuitarSancion/page.tsx b/app/(private)/QuitarSancion/page.tsx index 83c7dcf..42dc6ae 100644 --- a/app/(private)/QuitarSancion/page.tsx +++ b/app/(private)/QuitarSancion/page.tsx @@ -27,10 +27,7 @@ export default async function Page(props: { {errorMessage && }

Quitar Sanciones

-
- - -
+ ); } diff --git a/app/Components/Alta/registerAlta.css b/app/Components/Alta/registerAlta.css index f81c1d8..a14c892 100644 --- a/app/Components/Alta/registerAlta.css +++ b/app/Components/Alta/registerAlta.css @@ -8,6 +8,11 @@ color: #ffffff; } +.buttonInscription{ + background-color: #007a5b; + color: white; +} + .buttonSave:not(:disabled):hover { background-color: #001f5c; } diff --git a/app/Components/Alta/registerAlta.tsx b/app/Components/Alta/registerAlta.tsx index b1796ca..54578b9 100644 --- a/app/Components/Alta/registerAlta.tsx +++ b/app/Components/Alta/registerAlta.tsx @@ -4,23 +4,29 @@ import apiClient from "@/app/lib/apiClient"; import "./registerAlta.css"; import toast from "react-hot-toast"; +import { useRouter } from "next/navigation"; type Carrera = { id_carrera: number; carrera: string; }; -export default function RegisterAlta() { +interface urlProp { + numCount: string | null; +} + +export default function RegisterAlta(props: urlProp) { + const router = useRouter(); const [listMajor, setListMajor] = useState([]); const [saved, setSaved] = useState(false); const [form, setForm] = useState({ - cuenta: "", + cuenta: String(props.numCount), nombre: "", apellidoP: "", apellidoM: "", fecha: "", - email: "", + email: `${props.numCount}@pcpuma.acatlan.unam.mx`, genero: "", carrera: "", }); @@ -52,14 +58,23 @@ export default function RegisterAlta() { (value) => value.trim() !== "", ); - const handleSave = async (e: React.MouseEvent) => { e.preventDefault(); try { - await apiClient.post("/alumno", form); + const payload = { + id_cuenta: Number(form.cuenta), + nombre: `${form.apellidoP} ${form.apellidoM} ${form.nombre}`.trim(), + fecha_nacimiento: form.fecha.replaceAll("-", ""), + correo: form.email || undefined, + id_carrera: Number(form.carrera), + genero: form.genero || undefined, + }; + + await apiClient.post("/student", payload); + setSaved(true); - toast.error("Alumno registrado correctamente"); + toast.success("Alumno registrado correctamente"); } catch (error) { console.error("Error al guardar alumno:", error); toast.error("Error al guardar alumno"); @@ -68,7 +83,7 @@ export default function RegisterAlta() { const handleInscripcion = (e: React.MouseEvent) => { e.preventDefault(); - toast.success("Continuar con inscripción"); + router.push(`/Inscripcion?numAcount=${form.cuenta}`); }; return ( @@ -81,6 +96,7 @@ export default function RegisterAlta() { value={form.cuenta} onChange={handleChange} placeholder="Coloca un número de cuenta..." + required /> @@ -92,6 +108,7 @@ export default function RegisterAlta() { value={form.nombre} onChange={handleChange} placeholder="Coloca el nombre" + required /> @@ -103,6 +120,7 @@ export default function RegisterAlta() { value={form.apellidoP} onChange={handleChange} placeholder="Coloca el apellido paterno" + required /> @@ -114,6 +132,7 @@ export default function RegisterAlta() { value={form.apellidoM} onChange={handleChange} placeholder="Coloca el apellido materno" + required /> @@ -124,6 +143,7 @@ export default function RegisterAlta() { name="fecha" value={form.fecha} onChange={handleChange} + required /> @@ -140,17 +160,27 @@ export default function RegisterAlta() {
- - - - + + +
- {listMajor.map((c) => (
+ {!isCreating && ( +
+ setId(e.target.value)} + /> + +
+ )} {data && (
@@ -77,6 +165,7 @@ export default function Equipos() { setUbicacion(e.target.value)} /> @@ -84,12 +173,14 @@ export default function Equipos() { setNombre(e.target.value)} /> setIdArea(Number(e.target.value))} > @@ -114,12 +206,46 @@ export default function Equipos() {
- - + {isEditing ? ( + <> + + + + + ) : ( + <> + + + + )}
)} diff --git a/app/Components/Global/Toggle/Toggle.css b/app/Components/Global/Toggle/Toggle.css new file mode 100644 index 0000000..e69de29 diff --git a/app/Components/Global/Toggle/Toggle.tsx b/app/Components/Global/Toggle/Toggle.tsx index 6c3a237..8183867 100644 --- a/app/Components/Global/Toggle/Toggle.tsx +++ b/app/Components/Global/Toggle/Toggle.tsx @@ -26,6 +26,7 @@ export default function Toggle({ options, defaultView }: ToggleProps) { const params = new URLSearchParams(searchParams.toString()); params.set("key", key); + params.delete("machine"); router.replace(`${pathname}?${params.toString()}`); }; diff --git a/app/Components/Global/Toggle/ToggleTable.tsx b/app/Components/Global/Toggle/ToggleTable.tsx new file mode 100644 index 0000000..c308299 --- /dev/null +++ b/app/Components/Global/Toggle/ToggleTable.tsx @@ -0,0 +1,53 @@ +"use client"; + +import { usePathname, useSearchParams } from "next/navigation"; +import { useRouter } from "next/navigation"; +import { useState, ReactNode } from "react"; + +interface ToggleOption { + key: string; + label: string; + content: ReactNode; +} + +interface ToggleProps { + options: ToggleOption[]; + defaultView?: string; +} + +export default function ToggleTable({ options, defaultView }: ToggleProps) { + const router = useRouter(); + const searchParams = useSearchParams(); + const pathname = usePathname(); + const [view, setView] = useState(defaultView || options[0].key); + + const handleClick = (key: string) => { + setView(key); + + const params = new URLSearchParams(searchParams.toString()); + params.set("key", key); + + router.replace(`${pathname}?${params.toString()}`); + }; + + return ( +
+
+ {options.map((opt) => ( + + ))} +
+ +
+ {options.find((opt) => opt.key === view)?.content} +
+
+ ); +} +//IO diff --git a/app/Components/Impressions/impressions.tsx b/app/Components/Impressions/impressions.tsx index b0e9029..f551816 100644 --- a/app/Components/Impressions/impressions.tsx +++ b/app/Components/Impressions/impressions.tsx @@ -13,9 +13,10 @@ interface CostOption { interface ImpressionsProps { costs: CostOption[]; numAcount: number | null; + id_servicio: number; } -function Impressions({ costs, numAcount }: ImpressionsProps) { +function Impressions({ costs, numAcount, id_servicio }: ImpressionsProps) { const [pages, setPages] = useState(""); const [cost, setCost] = useState(""); @@ -47,13 +48,14 @@ function Impressions({ costs, numAcount }: ImpressionsProps) { id_cuenta: numAcount, numero_hojas: parseInt(pages), monto: parseInt(cost) * parseInt(pages), + id_servicio, }); - if (result.error) { - if (result.error === "Token inválido") handleLogout(); - else { - toast.error(result.error); - return; + if (result?.error) { + if (result.message === "Token inválido") { + handleLogout(); + } else { + toast.error(result.message); } return; } @@ -101,7 +103,10 @@ function Impressions({ costs, numAcount }: ImpressionsProps) { /> -
+
diff --git a/app/Components/Receipt/Receipt.tsx b/app/Components/Receipt/Receipt.tsx index c156f61..a270a4c 100644 --- a/app/Components/Receipt/Receipt.tsx +++ b/app/Components/Receipt/Receipt.tsx @@ -16,7 +16,8 @@ function Receipt({ numAcount }: ReceiptsProps) { const [folio, setFolio] = useState(""); const [amount, setAmount] = useState(""); - const [date, setDate] = useState(""); + const todayISO = new Date().toISOString().split("T")[0]; + const [date, setDate] = useState(todayISO); //restrict this month// const day = new Date(); diff --git a/app/Components/SearchEquipo.tsx b/app/Components/SearchEquipo.tsx index 80384a7..60c42c7 100644 --- a/app/Components/SearchEquipo.tsx +++ b/app/Components/SearchEquipo.tsx @@ -1,12 +1,57 @@ "use client"; -export default function SearchEquipo() { + +import { usePathname, useRouter, useSearchParams } from "next/navigation"; +import { useEffect, useState } from "react"; + +interface urlProp { + value: string | null; +} + +export default function SearchEquipo(props: urlProp) { + const [value, setValue] = useState(""); + const router = useRouter(); + const pathname = usePathname(); + const searchParams = useSearchParams(); + + useEffect(() => { + if (props.value) { + setValue(props.value); + } + }, [props.value]); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + const params = new URLSearchParams(searchParams.toString()); + if (value) { + params.set("machine", `${value}`); + router.push(`${pathname}?${params.toString()}`); + } + }; + return ( -
- -
- - -
-
+ <> +
+ +
+ { + const value = e.target.value; + if (/^\d*$/.test(value) && value.length <= 9) { + setValue(value); + } + }} + placeholder="Coloca un número de cuenta..." + inputMode="numeric" + pattern="[0-9]*" + /> + +
+
+ ); } +//IO \ No newline at end of file diff --git a/app/Components/Selection/Selection.tsx b/app/Components/Selection/Selection.tsx index 27630bc..26370d8 100644 --- a/app/Components/Selection/Selection.tsx +++ b/app/Components/Selection/Selection.tsx @@ -20,15 +20,15 @@ function Selection({ const options = [ { name: "WINDOWS", - img: "https://images.icon-icons.com/2235/PNG/512/windows_os_logo_icon_134678.png", + img: "/windows.png", }, { name: "MACINTOSH", - img: "https://upload.wikimedia.org/wikipedia/commons/f/fa/Apple_logo_black.svg", + img: "apple.png", }, { name: "PROFESORES", - img: "https://cdn-icons-png.flaticon.com/512/3135/3135715.png", + img: "teacher.png", }, ]; diff --git a/app/Components/Selection/SelectionCo.tsx b/app/Components/Selection/SelectionCo.tsx index ba51d12..7393ec8 100644 --- a/app/Components/Selection/SelectionCo.tsx +++ b/app/Components/Selection/SelectionCo.tsx @@ -15,19 +15,18 @@ function SelectionCo({ const options = [ { name: "WINDOWS", - img: "https://images.icon-icons.com/2235/PNG/512/windows_os_logo_icon_134678.png", + img: "/windows.png", }, { name: "MACINTOSH", - img: "https://upload.wikimedia.org/wikipedia/commons/f/fa/Apple_logo_black.svg", + img: "apple.png", }, { name: "PROFESORES", - img: "https://cdn-icons-png.flaticon.com/512/3135/3135715.png", + img: "teacher.png", }, ]; - // ✅ SOLO las inscritas const opcionesInscritas = options.filter((option) => plataformasInscritas.includes(option.name), ); diff --git a/app/Components/layout/BarNavigation/BarNavigation.tsx b/app/Components/layout/BarNavigation/BarNavigation.tsx index c6d9276..502fd4d 100644 --- a/app/Components/layout/BarNavigation/BarNavigation.tsx +++ b/app/Components/layout/BarNavigation/BarNavigation.tsx @@ -1,9 +1,9 @@ "use client"; import { useState } from "react"; -import "./BarNavigation.css"; import Link from "next/link"; +import "./BarNavigation.css"; -function BarNavigation() { +export default function BarNavigation() { const [openMenu, setOpenMenu] = useState(false); const [openSubMenu, setOpenSubMenu] = useState(null); @@ -169,28 +169,18 @@ function BarNavigation() { >
  • Inscritos
  • - { - e.stopPropagation(); - closeAllMenus(); - }} - > -
  • Bitacora y sanciones
  • -
  • { e.stopPropagation(); closeAllMenus(); }} > - Quitar sanción + Bitácora y sanciones
  • @@ -209,6 +199,4 @@ function BarNavigation() { ); } - -export default BarNavigation; //IO diff --git a/app/Components/layout/BarNavigation/BarNavigationServicio.tsx b/app/Components/layout/BarNavigation/BarNavigationServicio.tsx new file mode 100644 index 0000000..4b16f3f --- /dev/null +++ b/app/Components/layout/BarNavigation/BarNavigationServicio.tsx @@ -0,0 +1,119 @@ +"use client"; +import { useState } from "react"; +import Link from "next/link"; +import "./BarNavigation.css"; + +export default function BarNavigationServicio() { + const [openMenu, setOpenMenu] = useState(false); + const [openSubMenu, setOpenSubMenu] = useState(null); + + const toggleMenu = () => setOpenMenu(!openMenu); + const toggleSubMenu = (index: number) => { + if (typeof window !== "undefined" && window.innerWidth <= 1000) { + setOpenSubMenu(openSubMenu === index ? null : index); + } + }; + + const closeAllMenus = () => { + setOpenMenu(false); + setOpenSubMenu(null); + }; + + return ( + + ); +} +//IO diff --git a/app/Components/layout/Header/Header.tsx b/app/Components/layout/Header/Header.tsx index c077abf..e8a5b86 100644 --- a/app/Components/layout/Header/Header.tsx +++ b/app/Components/layout/Header/Header.tsx @@ -3,6 +3,7 @@ import Link from "next/link"; import BarNavigation from "../BarNavigation/BarNavigation"; import header from "./Header.module.css"; import { cookies } from "next/headers"; +import BarNavigationServicio from "../BarNavigation/BarNavigationServicio"; export default async function Header() { const cookieStore = await cookies(); @@ -28,10 +29,11 @@ export default async function Header() {
    - {role === "ADMINISTRADOR" && } + {role === "ADMINIS.TRADOR" && } {role === "SUPER ADMINISTRADOR (quita sanciones)" && } - {role === "SERVICIO SOCIAL" && } + {role === "ADMINISTRADOR" && } {role === "ATENCION A USUARIO" && } + {role === "SERVICIO SOCIAL" && }
    ); diff --git a/app/globals.css b/app/globals.css index 73a342d..781ae71 100644 --- a/app/globals.css +++ b/app/globals.css @@ -585,7 +585,6 @@ table td:last-child { align-items: center; font-size: 13px; cursor: pointer; - margin-right: 1rem; } .checkbox-grid input[type="checkbox"] { diff --git a/app/lib/getEquipoByCount.ts b/app/lib/getEquipoByCount.ts new file mode 100644 index 0000000..a041689 --- /dev/null +++ b/app/lib/getEquipoByCount.ts @@ -0,0 +1,19 @@ +import axios from "axios"; +import { envConfig } from "./config"; + +export async function getEquipoByCount(idCuenta: number) { + try { + const res = await axios.get( + `${envConfig.apiUrl}/bitacora/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 diff --git a/app/lib/postImpressions.ts b/app/lib/postImpressions.ts index 5c795f7..f0abae8 100644 --- a/app/lib/postImpressions.ts +++ b/app/lib/postImpressions.ts @@ -6,10 +6,15 @@ export async function PostImpressions(data: any) { return response.data; } catch (error: any) { const msg = - error.response?.data?.error || + error.response?.data?.message || error.message || "Error desconocido al cobrar impresión"; - return { error: msg }; + + return { + error: true, + message: msg, + statusCode: error.response?.status, + }; } } //IO diff --git a/public/apple.png b/public/apple.png new file mode 100644 index 0000000..af00ef5 Binary files /dev/null and b/public/apple.png differ diff --git a/public/teacher.png b/public/teacher.png new file mode 100644 index 0000000..418e26a Binary files /dev/null and b/public/teacher.png differ diff --git a/public/windows.png b/public/windows.png new file mode 100644 index 0000000..50d3d77 Binary files /dev/null and b/public/windows.png differ