Merge branch 'Lino' into Carlos
This commit is contained in:
@@ -1,13 +1,10 @@
|
||||
import styles from "./Page.module.css";
|
||||
|
||||
export default async function MesasDisponibles() {
|
||||
return (
|
||||
<section className="containerSection">
|
||||
|
||||
<div className={styles.tableContainer}>
|
||||
<table className={styles.machineTable}>
|
||||
<div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr style={{fontSize:"15px"}}>
|
||||
<tr style={{ fontSize: "15px" }}>
|
||||
<th>Mesa</th>
|
||||
<th>Activo</th>
|
||||
</tr>
|
||||
|
||||
@@ -76,6 +76,28 @@ export default function RegisterAlta() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="containerForm">
|
||||
<label className="label">Email</label>
|
||||
<input
|
||||
type="text"
|
||||
//value={user}
|
||||
//onChange={(e) => setUser(e.target.value)}
|
||||
placeholder="Coloca el email"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="containerForm">
|
||||
<label htmlFor="gender" className="label">
|
||||
Género
|
||||
</label>
|
||||
<select id="gender" name="gender">
|
||||
<option value="">Selecciona Género</option>
|
||||
<option value="masculino">Masculino</option>
|
||||
<option value="femenino">Femenino</option>
|
||||
<option value="otro">Otro</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="containerForm">
|
||||
<label className="label">Carrera</label>
|
||||
<select value={major} onChange={(e) => setMajor(e.target.value)}>
|
||||
@@ -88,7 +110,8 @@ export default function RegisterAlta() {
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button className="button buttonSearch">registrar</button>
|
||||
<button className="button buttonSearch">Guardar</button>
|
||||
<button className="button buttonSearch">Inscripcion</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,19 +1,34 @@
|
||||
"use client";
|
||||
import { SetStateAction, useState } from "react";
|
||||
import axios from "axios";
|
||||
import { SetStateAction, useEffect, useState } from "react";
|
||||
import { envConfig } from "../lib/config";
|
||||
|
||||
interface mesas {
|
||||
idMesa: number;
|
||||
active: number;
|
||||
}
|
||||
export default function AsignacionMesas() {
|
||||
const [tiempo, setTiempo] = useState("");
|
||||
const [mesa, setMesa] = useState<mesas[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const getMesa = async() => {
|
||||
const response = await axios.get(`${envConfig.apiUrl}/mesa/activo`);
|
||||
setMesa(response.data);
|
||||
};
|
||||
getMesa()
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<form className="containerForm">
|
||||
<label className="label">Mesas disponibles</label>
|
||||
<div className="groupInput">
|
||||
<select value={tiempo} onChange={(e) => setTiempo(e.target.value)}>
|
||||
<option value="">-- Mesas disponibles --</option>
|
||||
<option value="15">1 </option>
|
||||
<option value="30">3 </option>
|
||||
<option value="45">4 </option>
|
||||
<option value="60">22 </option>
|
||||
<option value="90">15 </option>
|
||||
<option value="120">20 </option>
|
||||
|
||||
{mesa.map((mesa, key) => (
|
||||
<option key={key} value={mesa.idMesa}>{mesa.idMesa}</option>
|
||||
))}
|
||||
</select>
|
||||
<button className="button buttonSearch" type="submit">
|
||||
Actualizar
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
"use client";
|
||||
import SearchDate from "../SearchDate/SearchDate";
|
||||
import { useState } from "react";
|
||||
|
||||
import styles from "./Page.module.css";
|
||||
|
||||
interface alumnos {
|
||||
tiempo_entrada: string;
|
||||
tiempo_asignado: string;
|
||||
@@ -14,27 +11,24 @@ function BitacoraAlumno() {
|
||||
const [alumnos, setAlumnos] = useState<alumnos[]>([]);
|
||||
return (
|
||||
<>
|
||||
<SearchDate />
|
||||
<div className={styles.tableContainer} style={{marginTop:"1rem"}}>
|
||||
<table className={styles.machineTable}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Tiempo Entrada</th>
|
||||
<th>Tiempo Asignado</th>
|
||||
<th>Ubicacion del equipo</th>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Tiempo Entrada</th>
|
||||
<th>Tiempo Asignado</th>
|
||||
<th>Ubicacion del equipo</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{alumnos.map((alumno, index) => (
|
||||
<tr key={index}>
|
||||
<td>{alumno.tiempo_entrada}</td>
|
||||
<td>{alumno.tiempo_asignado}</td>
|
||||
<td>{alumno.Ubicacion_equipo}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{alumnos.map((alumno, index) => (
|
||||
<tr key={index}>
|
||||
<td>{alumno.tiempo_entrada}</td>
|
||||
<td>{alumno.tiempo_asignado}</td>
|
||||
<td>{alumno.Ubicacion_equipo}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
import SearchDate from "../SearchDate/SearchDate";
|
||||
import { useState } from "react";
|
||||
|
||||
import styles from "./Page.module.css";
|
||||
|
||||
interface equipos {
|
||||
hora_entrada: string;
|
||||
min_utilizados: number;
|
||||
@@ -16,7 +14,6 @@ function BitacoraEquipo() {
|
||||
const [ubicacion_equipo, setUbicacionEquipo] = useState("");
|
||||
return (
|
||||
<>
|
||||
<SearchDate />
|
||||
<form className="containerForm">
|
||||
<label className="label">Ubicacion de equipo</label>
|
||||
|
||||
@@ -28,13 +25,11 @@ function BitacoraEquipo() {
|
||||
<option value="">-- Selecciona un equipo --</option>
|
||||
<option value="255">Equipo 255</option>
|
||||
</select>
|
||||
<button className="button buttonSearch" type="submit">
|
||||
Asignar
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div className={styles.tableContainer} style={{marginTop:"1rem"}}>
|
||||
<table className={styles.machineTable}>
|
||||
<SearchDate />
|
||||
<div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Hora Entrada</th>
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
import SearchDate from "../SearchDate/SearchDate";
|
||||
import { useState } from "react";
|
||||
|
||||
import styles from "./Page.module.css";
|
||||
|
||||
interface tables {
|
||||
no_mesa: number;
|
||||
no_cuenta: number;
|
||||
@@ -18,8 +16,8 @@ function BitacoraMesas() {
|
||||
return (
|
||||
<>
|
||||
<SearchDate />
|
||||
<div className={styles.tableContainer} style={{marginTop:"1rem"}}>
|
||||
<table className={styles.machineTable}>
|
||||
<div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Mesa</th>
|
||||
|
||||
@@ -5,24 +5,22 @@ import TableSancion from "./TableSancion";
|
||||
interface Student {
|
||||
id_cuenta: string;
|
||||
nombre: string;
|
||||
credito?: number;
|
||||
}
|
||||
|
||||
export default async function Sanciones(props: { student?: Student }) {
|
||||
const idCuenta = props?.student?.id_cuenta ?? null;
|
||||
export default function Sanciones({ student }: { student?: Student }) {
|
||||
const idCuenta = student?.id_cuenta ?? null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<SearchUser value={idCuenta} />
|
||||
{props.student && (
|
||||
{student && (
|
||||
<>
|
||||
<Information
|
||||
NoCuenta={props.student.id_cuenta}
|
||||
Nombre={props.student.nombre}
|
||||
/>
|
||||
|
||||
<TableSancion />
|
||||
<Information NoCuenta={student.id_cuenta} Nombre={student.nombre} />
|
||||
<TableSancion idCuenta={parseInt(student.id_cuenta)} />
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
//
|
||||
|
||||
@@ -1,73 +1,129 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import styles from "./Page.module.css";
|
||||
import axios from "axios";
|
||||
import toast from "react-hot-toast";
|
||||
import { envConfig } from "@/app/lib/config";
|
||||
|
||||
interface alumno_sancion {
|
||||
id_alumno_sancion: number;
|
||||
fecha_inicio: string;
|
||||
alumno: alumno;
|
||||
sancion: sancion;
|
||||
}
|
||||
|
||||
interface alumno {
|
||||
interface Alumno {
|
||||
id_cuenta: number;
|
||||
nombre: string;
|
||||
credito: number;
|
||||
}
|
||||
|
||||
interface sancion {
|
||||
interface Sancion {
|
||||
id_sancion: number;
|
||||
sancion: string;
|
||||
duracion: number;
|
||||
}
|
||||
|
||||
export default function TableSancion() {
|
||||
const [sanciones, setSanciones] = useState<any>();
|
||||
const [button, setButton] = useState<boolean>(false);
|
||||
interface AlumnoSancion {
|
||||
id_alumno_sancion: number;
|
||||
fecha_inicio: string;
|
||||
sancion: Sancion;
|
||||
}
|
||||
|
||||
// useEffect(() => {
|
||||
// const getSanciones = async () => {
|
||||
// const response = await axios.get("");
|
||||
// setSanciones(response);
|
||||
// };
|
||||
// getSanciones();
|
||||
// }, [button]);
|
||||
if (!envConfig.apiUrl) {
|
||||
console.error("API URL is not defined in envConfig");
|
||||
}
|
||||
|
||||
const handlebutton = () => {
|
||||
setButton(!button);
|
||||
export default function TableSancion({ idCuenta }: { idCuenta: number }) {
|
||||
const [sanciones, setSanciones] = useState<Sancion[]>([]);
|
||||
const [alumno, setAlumno] = useState<Alumno | null>(null);
|
||||
const [alumnoSanciones, setAlumnoSanciones] = useState<AlumnoSancion[]>([]);
|
||||
const [selectedSancion, setSelectedSancion] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
if (!idCuenta) return;
|
||||
|
||||
// Obtener sanciones del alumno
|
||||
axios
|
||||
.get(
|
||||
`${envConfig.apiUrl}/asignacionTiempo_test/alumno-sancion/${idCuenta}`
|
||||
)
|
||||
.then((res) => {
|
||||
setAlumno(res.data.student);
|
||||
setAlumnoSanciones(
|
||||
res.data.alusancion?.length ? res.data.alusancion : []
|
||||
);
|
||||
})
|
||||
.catch((err) =>
|
||||
toast.error("Error al obtener las sanciones del alumno:", err)
|
||||
);
|
||||
|
||||
// Obtener catálogo de sanciones
|
||||
axios
|
||||
.get(`${envConfig.apiUrl}/asignacionTiempo_test/sancion`)
|
||||
.then((res) => setSanciones(res.data))
|
||||
.catch((err) =>
|
||||
toast.error("Error al obtener el catálogo de sanciones:", err)
|
||||
);
|
||||
}, [idCuenta]);
|
||||
|
||||
const calcularFechaFin = (fechaInicio: string, duracionSemanas: number) => {
|
||||
const fecha = new Date(fechaInicio);
|
||||
fecha.setDate(fecha.getDate() + duracionSemanas * 7);
|
||||
return fecha.toLocaleDateString();
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.tableContainer} style={{margin:"1rem 0"}}>
|
||||
<table className={styles.machineTable}>
|
||||
<div style={{ margin: "1rem 0" }}>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Cuenta</th>
|
||||
<th>Motivo de la sanción</th>
|
||||
<th>Duracion (Semanas) </th>
|
||||
<th>Duración (Semanas)</th>
|
||||
<th>Fecha Sanción</th>
|
||||
<th>Podra utilizar el servicio hasta</th>
|
||||
<th>Podrá utilizar el servicio hasta</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
<tbody>
|
||||
{alumnoSanciones.length > 0 ? (
|
||||
alumnoSanciones.map((item) => (
|
||||
<tr key={item.id_alumno_sancion}>
|
||||
<td>{alumno?.id_cuenta}</td>
|
||||
<td>{item.sancion.sancion}</td>
|
||||
<td>{item.sancion.duracion}</td>
|
||||
<td>{new Date(item.fecha_inicio).toLocaleDateString()}</td>
|
||||
<td>
|
||||
{calcularFechaFin(item.fecha_inicio, item.sancion.duracion)}
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
<td colSpan={5}>No hay sanciones registradas</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<form className="containerForm">
|
||||
<div className="groupInput">
|
||||
<select>
|
||||
<select
|
||||
value={selectedSancion}
|
||||
onChange={(e) => setSelectedSancion(e.target.value)}
|
||||
>
|
||||
<option value="">-- Selecciona una sanción --</option>
|
||||
<option value="sancion 1">No cerrar sesion (Una semana)</option>
|
||||
{sanciones.map((sancion) => (
|
||||
<option key={sancion.id_sancion} value={sancion.id_sancion}>
|
||||
{sancion.sancion} ({sancion.duracion} semana/s)
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
<button className="button buttonSearch" style={{margin:"1rem 0"}} onClick={handlebutton}>
|
||||
|
||||
<button
|
||||
className="button buttonSearch"
|
||||
style={{ margin: "1rem 0" }}
|
||||
onClick={() => alert(`Sanción ${selectedSancion} aplicada`)}
|
||||
>
|
||||
Aplicar sanción
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
//IO
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
"use client";
|
||||
|
||||
import styles from "./style.module.css";
|
||||
import Cookies from "js-cookie";
|
||||
import { useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
export default function DownloadReporteXLSX() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleDownload = async () => {
|
||||
setLoading(true);
|
||||
const token = Cookies.get("token");
|
||||
const headers = { Authorization: `Bearer ${token}` };
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_API_URL}/equipos/reporteXLSX`,//Modificar
|
||||
{ headers }
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
toast.error("Error al descargar el archivo");
|
||||
return;
|
||||
}
|
||||
|
||||
const blob = await response.blob();
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = "Reporte Recibos.xlsx";
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
|
||||
window.URL.revokeObjectURL(url);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toast.error("Hubo un problema al descargar el archivo.");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
className={styles.downloadBtn}
|
||||
onClick={handleDownload}
|
||||
disabled={loading}
|
||||
>
|
||||
{loading ? (
|
||||
<div className={styles.loader}></div>
|
||||
) : (
|
||||
<img src="/excel.svg" alt="Excel" className={styles.iconExcel} width={30} height={30}/>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
.downloadBtn {
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
top:-35px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: #ffffff;
|
||||
border: 1px solid #ddd;
|
||||
border-bottom: none;
|
||||
padding: 5px 5px;
|
||||
cursor: pointer;
|
||||
border-radius: 10px 10px 0 0;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
transition: background 0.2s ease;
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
.downloadBtn:hover {
|
||||
background: #95d2b0;
|
||||
}
|
||||
|
||||
.downloadBtn:disabled {
|
||||
background: #a5a5a5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.loader {
|
||||
border: 3px solid #ffffff55;
|
||||
border-top: 3px solid white;
|
||||
border-radius: 50%;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
animation: spin 0.8s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@@ -8,75 +8,118 @@ interface Data {
|
||||
id_equipo: number;
|
||||
id_plataforma: number;
|
||||
id_area_ubicacion: number;
|
||||
ubicacion: string;
|
||||
}
|
||||
|
||||
interface Plataforma {
|
||||
id_plataforma: number;
|
||||
nombre: string;
|
||||
}
|
||||
|
||||
interface Area {
|
||||
id_area_ubicacion: number;
|
||||
area: string;
|
||||
extra: string;
|
||||
}
|
||||
|
||||
export default function Equipos() {
|
||||
const [Equipo, setEquipo] = useState("");
|
||||
const [Data, setData] = useState<Data | any>();
|
||||
const [id, setId] = useState("");
|
||||
const [data, setData] = useState<Data | null>(null);
|
||||
|
||||
const handleSubmit = async (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
const [plataforma, setPlataforma] = useState<Plataforma[]>([]);
|
||||
const [area, setArea] = useState<Area[]>([]);
|
||||
|
||||
const [ubicacion, setUbicacion] = useState("");
|
||||
const [nombre, setNombre] = useState("");
|
||||
|
||||
const [idPlataforma, setIdPlataforma] = useState<number | "">("");
|
||||
const [idArea, setIdArea] = useState<number | "">("");
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
// const response = await axios.get(
|
||||
// `${envConfig.apiUrl}/equipo/${Equipo}`
|
||||
// );
|
||||
|
||||
// if (!response) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
setData("response");
|
||||
const [equipoRes, plataformaRes, areaRes] = await Promise.all([
|
||||
axios.get(`${envConfig.apiUrl}/equipo/${id}`),
|
||||
axios.get(`${envConfig.apiUrl}/alumno-inscrito/plataforma`),
|
||||
axios.get(`${envConfig.apiUrl}/area-ubicacion`)
|
||||
]);
|
||||
|
||||
const equipo = equipoRes.data as Data;
|
||||
|
||||
setData(equipo);
|
||||
setNombre(equipo.nombre_equipo);
|
||||
setUbicacion(equipo.ubicacion);
|
||||
setIdPlataforma(equipo.id_plataforma);
|
||||
setIdArea(equipo.id_area_ubicacion);
|
||||
|
||||
setPlataforma(plataformaRes.data);
|
||||
setArea(areaRes.data);
|
||||
};
|
||||
|
||||
return (
|
||||
<form className="containerForm">
|
||||
<form className="containerForm" onSubmit={handleSubmit}>
|
||||
<div className="groupInput">
|
||||
<input
|
||||
placeholder="Numero de Equipo a buscar..."
|
||||
value={Equipo}
|
||||
onChange={(e) => {
|
||||
setEquipo(e.target.value);
|
||||
}}
|
||||
placeholder="Número de Equipo a buscar..."
|
||||
value={id}
|
||||
onChange={(e) => setId(e.target.value)}
|
||||
/>
|
||||
|
||||
<button type="button" className="button buttonSearch" onClick={handleSubmit}>
|
||||
<button type="submit" className="button buttonSearch">
|
||||
Buscar
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{Data && (
|
||||
{data && (
|
||||
<div>
|
||||
<label className="label">Ubicacion</label>
|
||||
<label className="label">Ubicación</label>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Coloca"
|
||||
value={Data.id_area_ubicacion}
|
||||
value={ubicacion}
|
||||
onChange={(e) => setUbicacion(e.target.value)}
|
||||
/>
|
||||
|
||||
<label className="label">Nombre</label>
|
||||
<input type="text" placeholder="Coloca " value={Data.nombre_equipo} />
|
||||
<input
|
||||
type="text"
|
||||
value={nombre}
|
||||
onChange={(e) => setNombre(e.target.value)}
|
||||
/>
|
||||
|
||||
<label className="label">Plataforma</label>
|
||||
<select>
|
||||
<option value="0">Elige</option>
|
||||
<option value="1">2</option>
|
||||
<option value="2">3</option>
|
||||
<option value="3">4</option>
|
||||
<option value="4">5</option>
|
||||
<option value="5">6</option>
|
||||
<select
|
||||
value={idPlataforma}
|
||||
onChange={(e) => setIdPlataforma(Number(e.target.value))}
|
||||
>
|
||||
<option value="">Elige</option>
|
||||
{plataforma.map((plat) => (
|
||||
<option key={plat.id_plataforma} value={plat.id_plataforma}>
|
||||
{plat.nombre}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
<label className="label">Area Ubicacion</label>
|
||||
<select>
|
||||
<option value="0">Elige</option>
|
||||
<option value="1">mmmmm</option>
|
||||
<option value="2">mmmmm</option>
|
||||
<option value="3">mmmmm</option>
|
||||
<option value="4">mmmmm</option>
|
||||
<option value="5">mmmmm</option>
|
||||
<label className="label">Área Ubicación</label>
|
||||
<select
|
||||
value={idArea}
|
||||
onChange={(e) => setIdArea(Number(e.target.value))}
|
||||
>
|
||||
<option value="">Elige</option>
|
||||
{area.map((a) => (
|
||||
<option key={a.id_area_ubicacion} value={a.id_area_ubicacion}>
|
||||
{a.area}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<div className="containerButton" style={{marginTop:"1rem"}}>
|
||||
<button className="button buttonSearch">Nuevo</button>
|
||||
<button className="button buttonSearch">Editar</button>
|
||||
|
||||
<div className="containerButton" style={{ marginTop: "1rem" }}>
|
||||
<button type="button" className="button buttonSearch">
|
||||
Nuevo
|
||||
</button>
|
||||
<button type="button" className="button buttonSearch">
|
||||
Editar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import styles from "./Page.module.css";
|
||||
|
||||
export default async function TableEquipos() {
|
||||
return (
|
||||
<section className="containerSection">
|
||||
|
||||
<div className={styles.tableContainer}>
|
||||
<table className={styles.machineTable}>
|
||||
<div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr style={{fontSize:"15px"}}>
|
||||
<tr style={{ fontSize: "15px" }}>
|
||||
<th>Ubicación</th>
|
||||
<th>Nombre</th>
|
||||
<th>Plataforma</th>
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
"use client";
|
||||
|
||||
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import SearchDate from "../../SearchDate/SearchDate";
|
||||
|
||||
interface urlProp {
|
||||
value: string | null;
|
||||
}
|
||||
|
||||
function SearchUserWithDate(props: urlProp) {
|
||||
const [numAcount, setnumAcount] = useState("");
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
useEffect(() => {
|
||||
if (props.value) {
|
||||
setnumAcount(props.value);
|
||||
}
|
||||
}, [props.value]);
|
||||
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
const params = new URLSearchParams(searchParams.toString());
|
||||
if (numAcount) {
|
||||
params.set("numAcount", `${numAcount}`);
|
||||
router.push(`${pathname}?${params.toString()}`);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<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;
|
||||
if (/^\d*$/.test(value) && value.length <= 9) {
|
||||
setnumAcount(value);
|
||||
}
|
||||
}}
|
||||
placeholder="Coloca un número de cuenta..."
|
||||
inputMode="numeric"
|
||||
pattern="[0-9]*"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
<SearchDate />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default SearchUserWithDate;
|
||||
//IO
|
||||
@@ -29,7 +29,7 @@ export default function Toggle({ options, defaultView }: ToggleProps) {
|
||||
|
||||
router.replace(`${pathname}?${params.toString()}`);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<section className="toggleSection">
|
||||
<div className="toggleGroup">
|
||||
@@ -44,10 +44,10 @@ export default function Toggle({ options, defaultView }: ToggleProps) {
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="padding">
|
||||
<div className="padding toggleContent">
|
||||
{options.find((opt) => opt.key === view)?.content}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
//IO
|
||||
//IO
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
.tableContainer {
|
||||
margin-top: 1rem;
|
||||
width: 100%;
|
||||
max-width: 450px;
|
||||
overflow-x: hidden;
|
||||
|
||||
@@ -101,7 +101,7 @@ function Impressions({ costs, numAcount }: ImpressionsProps) {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="groupInput">
|
||||
<div className="groupInput" style={{height:"35px", flexWrap:"nowrap"}}>
|
||||
<label className="label">Total:</label>
|
||||
<label
|
||||
style={{ width: "100%", minWidth: "200px", maxWidth: "500px" }}
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
"use client";
|
||||
import { useEffect, useState } from "react";
|
||||
import SelectAreas from "../SelectAreas";
|
||||
import { url } from "inspector";
|
||||
import SelectEquipo from "../SelectEquipo";
|
||||
import { envConfig } from "@/app/lib/config";
|
||||
import axios from "axios";
|
||||
|
||||
interface DatosEquipo {
|
||||
titulo: string;
|
||||
opcion: string[];
|
||||
tipo: "equipo" | "sala"; // Nuevo prop para diferenciar
|
||||
}
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
const programas = [
|
||||
"3D MAX STUDIO 2014",
|
||||
@@ -38,12 +30,37 @@ const programas = [
|
||||
"STATGRAOHICS Centurion XVI",
|
||||
];
|
||||
|
||||
export default function ProgramSelector({ titulo, opcion, tipo }: DatosEquipo) {
|
||||
const [seleccion, setSeleccion] = useState("");
|
||||
const [programas, setPrograma] = useState("");
|
||||
const caracteristicasPorEquipo: Record<string, string[]> = {
|
||||
"Equipo 1": ["3D MAX STUDIO 2014", "AUTOCAD", "MATLAB"],
|
||||
"Equipo 2": ["ADOBE CREATIVE SUITE", "COREL DRAW", "R Studio"],
|
||||
"Equipo 3": ["ARCHICAD 20", "EVIEWS Enterprise", "SPPS Statiscs"],
|
||||
"Equipo 4": ["MATHEMATICA", "MAPLE", "Maxima"],
|
||||
};
|
||||
|
||||
interface Equipos {
|
||||
id_equipo: number;
|
||||
id_plataforma: number;
|
||||
id_area_ubicacion: number;
|
||||
nombre_equipo: string;
|
||||
ubicacion: string;
|
||||
activo: boolean;
|
||||
ip: string;
|
||||
}
|
||||
|
||||
export default function ProgramSelector() {
|
||||
const [equipoSeleccionado, setEquipoSeleccionado] = useState("");
|
||||
const [checkboxes, setCheckboxes] = useState<Record<string, boolean>>(
|
||||
programas.reduce((acc, prog) => ({ ...acc, [prog]: false }), {})
|
||||
);
|
||||
const [equipo, setEquipo] = useState<Equipos[]>();
|
||||
|
||||
useEffect(() => {
|
||||
const getEquipos = async () => {
|
||||
const equipos = await axios.get(`${envConfig.apiUrl}/equipo`);
|
||||
setEquipo(equipos.data);
|
||||
};
|
||||
getEquipos();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const response = axios.get(
|
||||
@@ -52,8 +69,12 @@ export default function ProgramSelector({ titulo, opcion, tipo }: DatosEquipo) {
|
||||
setPrograma(response)
|
||||
});
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
setSeleccion(e.target.value);
|
||||
const nuevosCheckboxes: Record<string, boolean> = {};
|
||||
programas.forEach((prog) => {
|
||||
nuevosCheckboxes[prog] =
|
||||
caracteristicasPorEquipo[equipo]?.includes(prog) || false;
|
||||
});
|
||||
setCheckboxes(nuevosCheckboxes);
|
||||
};
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
@@ -68,14 +89,15 @@ export default function ProgramSelector({ titulo, opcion, tipo }: DatosEquipo) {
|
||||
|
||||
return (
|
||||
<form className="form-container" onSubmit={handleSubmit}>
|
||||
<label>{titulo}</label>
|
||||
|
||||
{/* 👇 Mostrar Select diferente según el tipo */}
|
||||
{tipo === "sala" ? (
|
||||
<SelectAreas url="https://venus.acatlan.unam.mx/asignacionTiempo_test/area-ubicacion" />
|
||||
) : (
|
||||
<SelectEquipo url="https://venus.acatlan.unam.mx/asignacionTiempo_test/equipo" />
|
||||
)}
|
||||
<select value={equipoSeleccionado} onChange={handleChangeEquipo}>
|
||||
<option value="">-- Selecciona un equipo --</option>
|
||||
{equipo &&
|
||||
equipo.map((eq, i) => (
|
||||
<option key={i} value={eq.id_equipo}>
|
||||
{eq.nombre_equipo}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
<div className="checkbox-grid">
|
||||
{programas.map((prog) => (
|
||||
|
||||
@@ -1,48 +1,73 @@
|
||||
"use client";
|
||||
import { useState } from "react";
|
||||
|
||||
import styles from "./Page.module.css";
|
||||
|
||||
interface quitarSanciones {
|
||||
id: number;
|
||||
interface Alumno {
|
||||
id_cuenta: number;
|
||||
nombre: string;
|
||||
motivo: string;
|
||||
duracion: number;
|
||||
fecha_sancion: string;
|
||||
utilizar_equipo: number;
|
||||
credito: number;
|
||||
}
|
||||
|
||||
function QuitarSancion() {
|
||||
const [quitarSanciones, SetQuitarSanciones] = useState<quitarSanciones[]>([]);
|
||||
interface Sancion {
|
||||
id_sancion: number;
|
||||
sancion: string;
|
||||
duracion: number;
|
||||
}
|
||||
|
||||
interface AlumnoSancion {
|
||||
id_alumno_sancion: number;
|
||||
fecha_inicio: string;
|
||||
sancion: Sancion;
|
||||
}
|
||||
|
||||
interface DataProps {
|
||||
student: Alumno;
|
||||
alusancion: AlumnoSancion[];
|
||||
}
|
||||
|
||||
interface Props {
|
||||
data: DataProps | null;
|
||||
}
|
||||
|
||||
function QuitarSancion({ data }: Props) {
|
||||
const calcularFechaFin = (fechaInicio: string, duracionSemanas: number) => {
|
||||
const fecha = new Date(fechaInicio);
|
||||
fecha.setDate(fecha.getDate() + duracionSemanas * 7);
|
||||
return fecha.toLocaleDateString();
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="containerSection">
|
||||
<div className={styles.tableContainer} style={{margin:"1rem 0"}}>
|
||||
<table className={styles.machineTable}>
|
||||
<div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>id</th>
|
||||
<th>ID</th>
|
||||
<th>Nombre</th>
|
||||
<th>Motivo Sanción</th>
|
||||
<th>Duracion (semanas) </th>
|
||||
<th>Duración (semanas)</th>
|
||||
<th>Fecha Sanción</th>
|
||||
<th>Podria utilizar el servicio hasta</th>
|
||||
<th>Podría utilizar el servicio hasta</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{quitarSanciones.map((quitarSanciones, index) => (
|
||||
<tr key={index}>
|
||||
<td>{quitarSanciones.id}</td>
|
||||
<td>{quitarSanciones.nombre}</td>
|
||||
<td>{quitarSanciones.motivo}</td>
|
||||
<td>{quitarSanciones.duracion}</td>
|
||||
<td>{quitarSanciones.fecha_sancion}</td>
|
||||
<td>{quitarSanciones.utilizar_equipo}</td>
|
||||
{data && data.alusancion.length !== 0 ? (
|
||||
data.alusancion.map((item) => (
|
||||
<tr key={item.id_alumno_sancion}>
|
||||
<td>{item.id_alumno_sancion}</td>
|
||||
<td>{data.student.nombre}</td>
|
||||
<td>{item.sancion.sancion}</td>
|
||||
<td>{item.sancion.duracion}</td>
|
||||
<td>{new Date(item.fecha_inicio).toLocaleDateString()}</td>
|
||||
<td>
|
||||
{calcularFechaFin(item.fecha_inicio, item.sancion.duracion)}
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
<td colSpan={6}>No hay sanciones registradas</td>
|
||||
</tr>
|
||||
))}
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<button className="button buttonSearch" style={{marginTop:"1rem"}}>Quitar Sanción</button>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
"use client";
|
||||
|
||||
import toast from "react-hot-toast";
|
||||
import { useState } from "react";
|
||||
import { PostReceipt } from "@/app/lib/postReceipt";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
import "./Receipt.css";
|
||||
import Selection from "../Selection/Selection";
|
||||
|
||||
interface ReceiptsProps {
|
||||
numAcount: number | null;
|
||||
}
|
||||
|
||||
export default function Inscripcion({ numAcount }: ReceiptsProps) {
|
||||
const router = useRouter();
|
||||
|
||||
const [folio, setFolio] = useState("");
|
||||
const [amount, setAmount] = useState("");
|
||||
const [date, setDate] = useState("");
|
||||
|
||||
//restrict this month//
|
||||
const day = new Date();
|
||||
const year = day.getFullYear();
|
||||
const month = day.getMonth();
|
||||
const today = day.getDate();
|
||||
|
||||
const minFecha = new Date(year, month, 1).toISOString().split("T")[0];
|
||||
const maxFecha = new Date(year, month, today).toISOString().split("T")[0];
|
||||
//restrict this month//
|
||||
|
||||
const handleSaveReceipt = async () => {
|
||||
if (!numAcount) {
|
||||
toast.error("busca de nuevo al estudiante");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!folio) {
|
||||
toast.error("Ingresa el folio del ticket");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!amount) {
|
||||
toast.error("Coloca el monto a depositar");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!date) {
|
||||
toast.error("Coloca la fecha");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await PostReceipt({
|
||||
id_cuenta: numAcount,
|
||||
folio_recibo: folio,
|
||||
monto: Number(amount),
|
||||
fecha_recibo: date,
|
||||
});
|
||||
|
||||
setFolio("");
|
||||
setAmount("");
|
||||
setDate("");
|
||||
|
||||
toast.success("Recibo guardado");
|
||||
router.refresh();
|
||||
} catch (err: any) {
|
||||
toast.error(String(err));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<section>
|
||||
<Selection />
|
||||
|
||||
<select
|
||||
style={{
|
||||
marginBottom: "1rem",
|
||||
maxWidth: "100px",
|
||||
minWidth: "100px",
|
||||
}}
|
||||
>
|
||||
<option value="0">con pago</option>
|
||||
<option value="1">sin pago</option>
|
||||
</select>
|
||||
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
handleSaveReceipt();
|
||||
}}
|
||||
>
|
||||
<div className="gap">
|
||||
<div className="groupInput">
|
||||
<label className="label">Ticket:</label>
|
||||
<input
|
||||
type="text"
|
||||
value={folio}
|
||||
onChange={(error) => {
|
||||
const value = error.target.value;
|
||||
if (/^\d*$/.test(value) && value.length <= 7) {
|
||||
setFolio(value);
|
||||
}
|
||||
}}
|
||||
placeholder="Numero de tiket..."
|
||||
inputMode="numeric"
|
||||
pattern="[0-9]*"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="groupInput">
|
||||
<label className="label">Monto:</label>
|
||||
<input
|
||||
type="text"
|
||||
value={amount}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value;
|
||||
|
||||
if (/^\d*\.?\d*$/.test(value)) {
|
||||
const numericValue = parseFloat(value);
|
||||
|
||||
if (value === "" || numericValue <= 1000) {
|
||||
setAmount(value);
|
||||
} else {
|
||||
toast.error("El monto no puede superar $1000.00");
|
||||
}
|
||||
}
|
||||
}}
|
||||
placeholder="Monto recibido..."
|
||||
inputMode="numeric"
|
||||
pattern="^\d*\.?\d+$"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="groupInput">
|
||||
<label className="label">Fecha de Pago:</label>
|
||||
<input
|
||||
type="date"
|
||||
value={date}
|
||||
onChange={(e) => setDate(e.target.value)}
|
||||
min={minFecha}
|
||||
max={maxFecha}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="containerButton">
|
||||
<button className="button buttonSearch">Inscribir</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
//IO
|
||||
@@ -129,7 +129,7 @@ function Receipt({ numAcount }: ReceiptsProps) {
|
||||
</div>
|
||||
|
||||
<div className="containerButton">
|
||||
<button className="button buttonSearch">Guardar</button>
|
||||
<button className="button buttonSearch" type="submit">Guardar</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -1,32 +1,93 @@
|
||||
"use client";
|
||||
import { useState } from "react";
|
||||
import styles from "./Page.module.css";
|
||||
|
||||
interface reportes {
|
||||
Servicio: "Plotter";
|
||||
Total: "$1440.00";
|
||||
import { useEffect, useState } from "react";
|
||||
import axios from "axios";
|
||||
import DownloadReporteXLSX from "../Dowload/Reporte";
|
||||
|
||||
interface Recibo {
|
||||
id_recibo: number;
|
||||
folio_recibo: string;
|
||||
fecha_recibo: string;
|
||||
fecha_registro: string;
|
||||
monto: string;
|
||||
user: {
|
||||
nombre: string;
|
||||
};
|
||||
}
|
||||
|
||||
function PorRecibos() {
|
||||
const [reportes, SetQuitarSanciones] = useState<reportes[]>([]);
|
||||
interface Props {
|
||||
desde: string | null;
|
||||
hasta: string | null;
|
||||
}
|
||||
|
||||
function PorRecibos({ desde, hasta }: Props) {
|
||||
const [recibos, setRecibos] = useState<Recibo[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!desde || !hasta) return;
|
||||
|
||||
const fetchRecibos = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await axios.post(
|
||||
`${process.env.NEXT_PUBLIC_API_URL}/recibo/rango`,
|
||||
{ desde, hasta }
|
||||
);
|
||||
|
||||
setRecibos(res.data);
|
||||
} catch (error) {
|
||||
console.error("Error al obtener recibos", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchRecibos();
|
||||
}, [desde, hasta]);
|
||||
|
||||
return (
|
||||
<div className={styles.tableContainer}>
|
||||
<table className={styles.machineTable}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Servicio</th>
|
||||
<th>Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{reportes.map((reporte, index) => (
|
||||
<tr key={index}>
|
||||
<td>{reporte.Servicio}</td>
|
||||
<td>{reporte.Total}</td>
|
||||
<div style={{ position: "relative" }}>
|
||||
{recibos.length > 0 && <DownloadReporteXLSX />}
|
||||
<div
|
||||
style={{
|
||||
overflow: "auto",
|
||||
height: "270px",
|
||||
scrollbarColor: "#2563eb white",
|
||||
}}
|
||||
>
|
||||
{loading && <p>Cargando...</p>}
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Folio</th>
|
||||
<th>Monto</th>
|
||||
<th>fecha recibo</th>
|
||||
<th>fecha registro</th>
|
||||
<th>Usuario</th>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{!loading && recibos.length === 0 && (
|
||||
<tr>
|
||||
<td colSpan={2}>No hay recibos en este rango</td>
|
||||
</tr>
|
||||
)}
|
||||
|
||||
{recibos.map((recibo) => (
|
||||
<tr key={recibo.id_recibo}>
|
||||
<td>{recibo.folio_recibo}</td>
|
||||
<td>${Number(recibo.monto).toFixed(2)}</td>
|
||||
<td>{recibo.fecha_recibo}</td>
|
||||
<td>{recibo.fecha_registro}</td>
|
||||
<td>{recibo.user.nombre}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,43 +1,86 @@
|
||||
"use client";
|
||||
import { useState } from "react";
|
||||
import styles from "./Page.module.css";
|
||||
|
||||
interface servicios {
|
||||
folio_recibo: "100255";
|
||||
monto: "40.00";
|
||||
fecha_recibo: "10/10/2025";
|
||||
fecha_registro: "10/10/2025 05:32:20 pm";
|
||||
usuario: "modulo1";
|
||||
import { useEffect, useState } from "react";
|
||||
import axios from "axios";
|
||||
import DownloadReporteXLSX from "../Dowload/Reporte";
|
||||
|
||||
|
||||
interface ServicioReporte {
|
||||
id_servicio: number;
|
||||
servicio: string;
|
||||
total: string;
|
||||
}
|
||||
|
||||
function PorServicios() {
|
||||
const [recibos, setRecibos] = useState<servicios[]>([]);
|
||||
interface Props {
|
||||
desde: string | null;
|
||||
hasta: string | null;
|
||||
}
|
||||
|
||||
function PorServicio({ desde, hasta }: Props) {
|
||||
const [servicios, setServicios] = useState<ServicioReporte[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!desde || !hasta) return;
|
||||
|
||||
const fetchServicios = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await axios.post(
|
||||
`${process.env.NEXT_PUBLIC_API_URL}/detalle-servicio/rango`,
|
||||
{ desde, hasta }
|
||||
);
|
||||
|
||||
setServicios(res.data);
|
||||
} catch (error) {
|
||||
console.error("Error al obtener reporte por servicio", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchServicios();
|
||||
}, [desde, hasta]);
|
||||
|
||||
return (
|
||||
<div className={styles.tableContainer}>
|
||||
<table className={styles.machineTable}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Folio Recibo</th>
|
||||
<th>Monto</th>
|
||||
<th>Fecha Recibo</th>
|
||||
<th>Fecha Registro</th>
|
||||
<th>Usuario</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{recibos.map((recibo, index) => (
|
||||
<tr key={index}>
|
||||
<td>{recibo.folio_recibo}</td>
|
||||
<td>{recibo.monto}</td>
|
||||
<td>{recibo.fecha_recibo}</td>
|
||||
<td>{recibo.fecha_registro}</td>
|
||||
<td>{recibo.usuario}</td>
|
||||
<div style={{ position: "relative" }}>
|
||||
{servicios.length > 0 && <DownloadReporteXLSX />}
|
||||
|
||||
<div
|
||||
style={{
|
||||
overflow: "auto",
|
||||
height: "270px",
|
||||
scrollbarColor: "#2563eb white",
|
||||
}}
|
||||
>
|
||||
{loading && <p>Cargando...</p>}
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Servicio</th>
|
||||
<th>Monto total</th>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{!loading && servicios.length === 0 && (
|
||||
<tr>
|
||||
<td colSpan={2}>No hay servicios en este rango</td>
|
||||
</tr>
|
||||
)}
|
||||
|
||||
{servicios.map((servicio) => (
|
||||
<tr key={servicio.id_servicio}>
|
||||
<td>{servicio.servicio}</td>
|
||||
<td>${Number(servicio.total).toFixed(2)}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default PorServicios;
|
||||
export default PorServicio;
|
||||
|
||||
@@ -1,51 +1,46 @@
|
||||
"use client";
|
||||
'use client';
|
||||
|
||||
import { useState } from "react";
|
||||
import { useState } from 'react';
|
||||
|
||||
function SearchDateBetween() {
|
||||
const [startDate, setStartDate] = useState("");
|
||||
const [endDate, setEndDate] = useState("");
|
||||
interface Props {
|
||||
onSearch: (desde: string, hasta: string) => void;
|
||||
}
|
||||
|
||||
function SearchDateBetween({ onSearch }: Props) {
|
||||
const [startDate, setStartDate] = useState('');
|
||||
const [endDate, setEndDate] = useState('');
|
||||
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
console.log("Fecha inicio:", startDate);
|
||||
console.log("Fecha fin:", endDate);
|
||||
// Aquí puedes hacer la lógica para buscar entre fechas
|
||||
if (!startDate || !endDate) return;
|
||||
onSearch(startDate, endDate);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<form className="containerForm" onSubmit={handleSubmit}>
|
||||
<label className="label">Desde</label>
|
||||
<div className="groupInput">
|
||||
<input
|
||||
type="date"
|
||||
value={startDate}
|
||||
onChange={(e) => setStartDate(e.target.value)}
|
||||
placeholder="Fecha inicio"
|
||||
/>
|
||||
</div>
|
||||
<label className="label">Hasta</label>
|
||||
<div className="groupInput">
|
||||
<input
|
||||
type="date"
|
||||
value={endDate}
|
||||
onChange={(e) => setEndDate(e.target.value)}
|
||||
placeholder="Fecha fin"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
className="button buttonSearch "
|
||||
type="submit"
|
||||
style={{ margin: "1rem 0 " }}
|
||||
>
|
||||
Buscar
|
||||
</button>
|
||||
</form>
|
||||
</>
|
||||
<form className="containerForm" onSubmit={handleSubmit}>
|
||||
<label className="label">Desde</label>
|
||||
<div className="groupInput">
|
||||
<input
|
||||
type="date"
|
||||
value={startDate}
|
||||
onChange={(e) => setStartDate(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<label className="label">Hasta</label>
|
||||
<div className="groupInput">
|
||||
<input
|
||||
type="date"
|
||||
value={endDate}
|
||||
onChange={(e) => setEndDate(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button className="button buttonSearch" type="submit" style={{marginBottom:"10px"}}>
|
||||
Buscar
|
||||
</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
export default SearchDateBetween;
|
||||
|
||||
// By Tyrannuss
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
"use client";
|
||||
import axios from "axios";
|
||||
import { useEffect, useState } from "react";
|
||||
import { envConfig } from "../lib/config";
|
||||
|
||||
if (!envConfig.apiUrl) {
|
||||
console.error("API URL is not defined in envConfig");
|
||||
}
|
||||
|
||||
interface proms {
|
||||
url: string;
|
||||
@@ -12,9 +17,13 @@ export default function SelectAreas(props: proms) {
|
||||
|
||||
useEffect(() => {
|
||||
axios
|
||||
.get(props.url)
|
||||
.then((data) => setAreas(data.data))
|
||||
.catch((error) => console.error("Error al traer las áreas:", error));
|
||||
.get(`${envConfig.apiUrl}/area-ubicacion`)
|
||||
.then((data) => {
|
||||
setAreas(data.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error al traer las áreas:", error);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,17 +1,24 @@
|
||||
"use client";
|
||||
import apiClient from "@/app/lib/apiClient";
|
||||
import { useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
export default function ChangePassword() {
|
||||
const [pass, setPass] = useState("");
|
||||
const [newPass, setNewPass] = useState("");
|
||||
const [password, setPass] = useState("");
|
||||
const [newPassword, setNewPass] = useState("");
|
||||
const [confirmNewPass, setconfirmNewPass] = useState("");
|
||||
|
||||
const handleChangePass = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault;
|
||||
const data = { pass, newPass, confirmNewPass };
|
||||
e.preventDefault();
|
||||
const data = { password, newPassword };
|
||||
|
||||
await apiClient.post("/user/create", data);
|
||||
try{
|
||||
const response = await apiClient.post("/user/changePassword", data);
|
||||
toast.success( `${response.data.message}`)
|
||||
}catch{
|
||||
toast.error( `No es la contraseña actual`)
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -21,7 +28,8 @@ export default function ChangePassword() {
|
||||
<label className="label">Contraseña actual</label>
|
||||
<input
|
||||
placeholder="Coloca tu contraseña..."
|
||||
value={pass}
|
||||
value={password}
|
||||
type="password"
|
||||
onChange={(e) => {
|
||||
setPass(e.target.value);
|
||||
}}
|
||||
@@ -33,7 +41,8 @@ export default function ChangePassword() {
|
||||
<label className="label">Nueva contraseña</label>
|
||||
<input
|
||||
placeholder="Coloca tu nueva contraseña..."
|
||||
value={newPass}
|
||||
value={newPassword}
|
||||
type="password"
|
||||
onChange={(e) => {
|
||||
setNewPass(e.target.value);
|
||||
}}
|
||||
@@ -46,6 +55,7 @@ export default function ChangePassword() {
|
||||
<input
|
||||
placeholder="Coloca tu nueva contraseña..."
|
||||
value={confirmNewPass}
|
||||
type="password"
|
||||
onChange={(e) => {
|
||||
setconfirmNewPass(e.target.value);
|
||||
}}
|
||||
@@ -57,7 +67,6 @@ export default function ChangePassword() {
|
||||
<button
|
||||
className="button buttonSearch"
|
||||
style={{ maxWidth: "100%", width: "100%" }}
|
||||
type="submit"
|
||||
>
|
||||
Confirmar
|
||||
</button>
|
||||
@@ -65,7 +74,6 @@ export default function ChangePassword() {
|
||||
<button
|
||||
className="button buttonCancel"
|
||||
style={{ maxWidth: "100%", width: "100%" }}
|
||||
type="submit"
|
||||
>
|
||||
Cancelar
|
||||
</button>
|
||||
@@ -74,3 +82,4 @@ export default function ChangePassword() {
|
||||
</section>
|
||||
);
|
||||
}
|
||||
//IO
|
||||
@@ -33,9 +33,11 @@ function Login() {
|
||||
const token = data.access_token;
|
||||
const payload = JSON.parse(atob(token.split(".")[1]));
|
||||
const usuario = payload.usuario;
|
||||
const role = payload.role;
|
||||
|
||||
document.cookie = `token=${token}; path=/; SameSite=Strict`;
|
||||
document.cookie = `user=${usuario}; path=/; SameSite=Strict`;
|
||||
document.cookie = `role=${role}; path=/; SameSite=Strict`;
|
||||
|
||||
toast.success("Inicio de sesión exitoso");
|
||||
router.push("/Impresiones");
|
||||
@@ -45,7 +47,7 @@ function Login() {
|
||||
return (
|
||||
<section className="centerGrid containerSection">
|
||||
<form className="login" onSubmit={handleLogin}>
|
||||
<h2 className="title">Inicio de sesión</h2>
|
||||
<h2 className="title">Bienvenido a AT</h2>
|
||||
|
||||
<div className="containerInput">
|
||||
<label className="label">Usuario</label>
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
color: transparent;
|
||||
padding: 2rem;
|
||||
font-size: 0px;
|
||||
top: 15px;
|
||||
}
|
||||
|
||||
.button-logout::after {
|
||||
|
||||
@@ -10,6 +10,9 @@ export default function Logout() {
|
||||
|
||||
const handleLogout = () => {
|
||||
Cookies.remove("token", { path: "/" });
|
||||
Cookies.remove("role", { path: "/" });
|
||||
Cookies.remove("user", { path: "/" });
|
||||
|
||||
router.push("/");
|
||||
};
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
.barNavigation {
|
||||
text-align: center;
|
||||
background-color: #003e79;
|
||||
padding: 0 20px;
|
||||
background-color: #ffffff;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
min-height: 40px;
|
||||
height: 45px;
|
||||
max-height: 40px;
|
||||
}
|
||||
|
||||
.barNavigation ul {
|
||||
@@ -13,24 +13,23 @@
|
||||
justify-content: space-around;
|
||||
width: 100%;
|
||||
gap: 10px;
|
||||
transition: width 0.5 ease-in-out;
|
||||
}
|
||||
|
||||
.barNavigation li {
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
color: white;
|
||||
color: #585858;
|
||||
width: 100%;
|
||||
transition: background-color 0.3s ease;
|
||||
transition: background-color 0.3s ease, border 0.3s ease;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.barNavigation li:hover {
|
||||
background-color: #bd8c01;
|
||||
}
|
||||
|
||||
.barNavigation ul.active {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
background-color: #f3f4f6;
|
||||
color: black !important;
|
||||
border-radius: 8px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.menuToggle {
|
||||
@@ -40,13 +39,12 @@
|
||||
gap: 4px;
|
||||
cursor: pointer;
|
||||
padding: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.menuToggle div {
|
||||
width: 25px;
|
||||
height: 3px;
|
||||
background-color: white;
|
||||
background-color: #000000;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
@@ -68,7 +66,10 @@
|
||||
flex-direction: column;
|
||||
top: 100%;
|
||||
width: 100%;
|
||||
background-color: #003e79;
|
||||
min-width: 200px;
|
||||
border: 1px solid #cfcfcf;
|
||||
border-radius: 8px;
|
||||
background-color: #ffffff;
|
||||
transition: color 0.3s ease;
|
||||
transition: opacity 0.4s ease, max-height 0.4s ease;
|
||||
}
|
||||
@@ -83,7 +84,7 @@
|
||||
}
|
||||
|
||||
.subMenu ul:hover {
|
||||
color: #5b8cc9;
|
||||
color: #101113;
|
||||
}
|
||||
|
||||
.subMenu:hover ul {
|
||||
@@ -100,18 +101,26 @@
|
||||
}
|
||||
|
||||
.subMenu span {
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: rgb(255, 255, 255);
|
||||
font-size: 2rem;
|
||||
color: #545454;
|
||||
font-size: 1.5rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.subMenu:hover > span,
|
||||
.subMenu > a:hover > span {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.containerLinks {
|
||||
padding: 0 !important;
|
||||
border-radius: 0 0 4px 4px;
|
||||
top: 20px;
|
||||
}
|
||||
|
||||
.links {
|
||||
@@ -129,7 +138,7 @@ tbody {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
@media (max-width: 1000px) {
|
||||
.menuToggle {
|
||||
display: flex;
|
||||
position: relative;
|
||||
@@ -149,13 +158,36 @@ tbody {
|
||||
|
||||
.barNavigation ul {
|
||||
position: absolute;
|
||||
background-color: #003e79;
|
||||
top: 76px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
flex-direction: column;
|
||||
padding: 10px 0;
|
||||
align-items: center;
|
||||
display: none;
|
||||
z-index: 1;
|
||||
height: auto;
|
||||
z-index: 10;
|
||||
|
||||
transform: translateY(-15px);
|
||||
opacity: 0;
|
||||
|
||||
pointer-events: none;
|
||||
|
||||
transition: transform 0.35s ease-in-out, opacity 0.35s ease-in-out,
|
||||
visibility 0.35s ease-in-out;
|
||||
}
|
||||
|
||||
.barNavigation ul.active {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.containerLinks {
|
||||
top: 20px;
|
||||
}
|
||||
|
||||
.barNavigation {
|
||||
@@ -163,17 +195,34 @@ tbody {
|
||||
}
|
||||
|
||||
.barNavigation li:hover {
|
||||
background-color: #d59f0f;
|
||||
color: white;
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.subMenu ul {
|
||||
width: 100%;
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
top: 0px;
|
||||
width: 100%;
|
||||
background-color: rgb(1, 92, 184);
|
||||
|
||||
max-height: 0;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
|
||||
transform: translateY(-5px);
|
||||
|
||||
transition: max-height 0.35s ease, opacity 0.3s ease, transform 0.3s ease;
|
||||
}
|
||||
|
||||
.subMenu.open ul {
|
||||
max-height: 500px;
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.subMenu ul {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
thead,
|
||||
|
||||
@@ -9,7 +9,7 @@ function BarNavigation() {
|
||||
|
||||
const toggleMenu = () => setOpenMenu(!openMenu);
|
||||
const toggleSubMenu = (index: number) => {
|
||||
if (typeof window !== "undefined" && window.innerWidth <= 800) {
|
||||
if (typeof window !== "undefined" && window.innerWidth <= 1000) {
|
||||
setOpenSubMenu(openSubMenu === index ? null : index);
|
||||
}
|
||||
};
|
||||
@@ -24,16 +24,13 @@ function BarNavigation() {
|
||||
|
||||
<ul className={openMenu ? "active" : ""}>
|
||||
<li className={`subMenu ${openSubMenu === 0 ? "open" : ""}`}>
|
||||
<span onClick={() => toggleSubMenu(0)}>Inscripción</span>
|
||||
<span onClick={() => toggleSubMenu(0)}>Inscripciónes</span>
|
||||
<ul className="containerLinks" onClick={toggleMenu}>
|
||||
<Link href="/Alta" className="links">
|
||||
<li>Alta</li>
|
||||
</Link>
|
||||
<Link href="/AgregarTiempo" className="links">
|
||||
<li>Agregar Tiempo</li>
|
||||
</Link>
|
||||
<Link href="/Inscripcion" className="links">
|
||||
<li>Inscripción</li>
|
||||
<li>Inscripción Usuario</li>
|
||||
</Link>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
.logo {
|
||||
position: relative;
|
||||
filter: invert(1);
|
||||
z-index: 3;
|
||||
margin: 5px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.logoContainer {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-width: 250px;
|
||||
min-width: 200px;
|
||||
height: auto;
|
||||
aspect-ratio: 250/70;
|
||||
}
|
||||
|
||||
.cedetecContainer {
|
||||
@@ -26,16 +34,82 @@
|
||||
content: "";
|
||||
top: 0;
|
||||
left: -20px;
|
||||
width: 65%;
|
||||
height: 100%;
|
||||
min-width: 300px;
|
||||
background: linear-gradient(to right, #bd8c01, #f9f9f9);
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
background: linear-gradient(to right, #bd8c01 40%, #fff 100%);
|
||||
transform: skew(-45deg);
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.header {
|
||||
width: 100vw;
|
||||
background-color: #fff;
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
padding: 0 10px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
.header::after {
|
||||
content: "";
|
||||
top: 0;
|
||||
left: -60px;
|
||||
position: absolute;
|
||||
background-color: #003e79;
|
||||
height: 100%;
|
||||
min-width: 350px;
|
||||
transform: skew(45deg);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.header::before {
|
||||
content: "";
|
||||
top: 0;
|
||||
left: -40px;
|
||||
position: absolute;
|
||||
background-color: rgb(1, 92, 184);
|
||||
height: 100%;
|
||||
width: 10%;
|
||||
min-width: 350px;
|
||||
transform: skew(45deg);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.containerBarNav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding-left: 110px;
|
||||
max-height: 60%;
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
.yellowPart {
|
||||
left: 0;
|
||||
background: #bd8c01;
|
||||
transform: skew(0deg);
|
||||
}
|
||||
.header::after {
|
||||
left: -100px;
|
||||
}
|
||||
.header::before {
|
||||
left: -80px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.header::after {
|
||||
min-width: 100%;
|
||||
transform: skew(25deg);
|
||||
}
|
||||
.header::before {
|
||||
min-width: 100%;
|
||||
transform: skew(25deg);
|
||||
}
|
||||
.containerBarNav {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,48 @@
|
||||
"use client";
|
||||
import Image from "next/image";
|
||||
import header from "./Header.module.css";
|
||||
import Link from "next/link";
|
||||
import BarNavigation from "../BarNavigation/BarNavigation";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
function Header() {
|
||||
const [role, setRole] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
const cookies = Object.fromEntries(
|
||||
document.cookie.split("; ").map((c) => c.split("="))
|
||||
);
|
||||
setRole(cookies.role || "");
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<header>
|
||||
<header className={header.header}>
|
||||
<Link
|
||||
href="https://www.unam.mx/"
|
||||
target="_blank"
|
||||
className={header.center}
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
margin: "10px 5px",
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
className={header.logo}
|
||||
src="/logo_fes.png"
|
||||
alt="Logo FES"
|
||||
width={200}
|
||||
height={50}
|
||||
/>
|
||||
<div className={header.logoContainer}>
|
||||
<Image
|
||||
className={header.logo}
|
||||
src="/logo_fes.png"
|
||||
alt="Logo FES"
|
||||
fill
|
||||
style={{ objectFit: "contain" }}
|
||||
/>
|
||||
</div>
|
||||
</Link>
|
||||
<div className={header.yellowPart}></div>
|
||||
<div className={header.cedetecContainer}>
|
||||
<Image
|
||||
src="/cedetec.jpg"
|
||||
alt="Image of CEDETEC"
|
||||
width={300}
|
||||
height={71}
|
||||
/>
|
||||
<div className={header.containerBarNav}>
|
||||
{role === "ADMINISTRADOR" && <BarNavigation />}
|
||||
{role === "SUPER ADMINISTRADOR (quita sanciones)" && <BarNavigation />}
|
||||
{role === "SERVICIO SOCIAL" && <BarNavigation/>}
|
||||
{role === "ATENCION A USUARIO" && <BarNavigation/>}
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user