Merge branch 'Lino' of https://github.com/IO420/Nexus into Carlos

This commit is contained in:
2025-10-10 10:50:07 -06:00
73 changed files with 1286 additions and 876 deletions
+11 -7
View File
@@ -1,13 +1,15 @@
"use client";
import { useState } from "react";
import "@/app/globals.css"
export default function Areas() {
const [area, setArea] = useState(""); // Área seleccionada
const [activo, setActivo] = useState(false); // Checkbox Activo
const [mantenimiento, setMantenimiento] = useState(false); // Checkbox Mantenimiento
const [mensaje, setMensaje] = useState(""); // Mensaje dinámico
const handleActualizar = (e: React.FormEvent) => {
const handleActualizar = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (!area) {
@@ -35,11 +37,10 @@ export default function Areas() {
return (
<form className="containerForm" onSubmit={handleActualizar}>
<label className="label">Áreas disponibles:</label>
<label className="label">Áreas</label>
<div className="groupInput">
{/* Select de áreas */}
<select value={area} onChange={(e) => setArea(e.target.value)}>
<option value="">-- Áreas disponibles --</option>
<option value="">-- Áreas --</option>
<option value="PECERA">PECERA</option>
<option value="JAULA">JAULA</option>
<option value="HUACAL">HUACAL</option>
@@ -48,13 +49,15 @@ export default function Areas() {
</select>
{/* Checkboxes */}
<div className="checkbox-grid">
<div className="checkbox" style={{ marginTop: "10px" }}>
<label style={{ marginRight: "10px" }}>
<label style={{ marginRight: "10px"}}>
<input
type="checkbox"
checked={activo}
onChange={(e) => setActivo(e.target.checked)}
/>
/>
Activo
</label>
<label>
@@ -62,10 +65,11 @@ export default function Areas() {
type="checkbox"
checked={mantenimiento}
onChange={(e) => setMantenimiento(e.target.checked)}
/>
/>
Mantenimiento
</label>
</div>
</div>
{/* Botón Actualizar */}
<button
@@ -6,7 +6,7 @@ function Equipos() {
const [tiempo, setTiempo] = useState("");
const [mensaje, setMensaje] = useState("");
const handleBuscar = (e: React.FormEvent) => {
const handleBuscar = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (!cuenta) {
setMensaje("Ingresa un número de cuenta antes de buscar");
@@ -15,7 +15,7 @@ function Equipos() {
setMensaje(`Buscando información del No. de cuenta: ${cuenta}`);
};
const handleAsignar = (e: React.FormEvent) => {
const handleAsignar = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (!tiempo) {
setMensaje("Selecciona un equipo antes de asignar");
@@ -1,12 +1,14 @@
"use client";
import { useState } from "react";
import "@/app/globals.css"
function Mesas() {
const [mesa, setMesa] = useState(""); // Mesa seleccionada
const [mantenimiento, setMantenimiento] = useState(false); // Checkbox Mantenimiento
const [mensaje, setMensaje] = useState(""); // Mensaje dinámico
const handleConfirmar = (e: React.FormEvent) => {
const handleConfirmar = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (!mesa) {
@@ -26,7 +28,7 @@ function Mesas() {
return (
<form className="containerForm" onSubmit={handleConfirmar}>
<label className="label">Mesas disponibles:</label>
<div className="groupInput">
<div className="groupInput" style={{display:"flex", flexDirection:"column"}}>
{/* Select de mesas */}
<select value={mesa} onChange={(e) => setMesa(e.target.value)}>
<option value="">-- Mesas disponibles --</option>
@@ -0,0 +1,33 @@
import styles from "./Page.module.css";
export default async function MesasDisponibles() {
return (
<section className="containerSection">
<div className={styles.tableContainer}>
<table className={styles.machineTable}>
<thead>
<tr style={{fontSize:"15px"}}>
<th>Mesa</th>
<th>Activo</th>
</tr>
</thead>
<tbody>
{/* {machines.map((machine, index) => (
<tr key={index}>
<td>{machine.ubicacion}</td>
<td>{machine.nombre}</td>
<td>{machine.plataforma}</td>
<td>{machine.area}</td>
<td className={machine.disponible ? "disponible" : ""}>
{machine.disponible ? "si" : "no"}
</td>
</tr>
))} */}
</tbody>
</table>
</div>
</section>
);
}
//IO
@@ -0,0 +1,70 @@
.tableContainer {
overflow-y: auto;
overflow-x: auto;
border-radius: 4px;
border: 1px solid #cfcfcf;
max-height: 430px;
scrollbar-color: rgb(1, 92, 184) rgba(0, 0, 0, 0);
background-color: #f9f9f9;
width: 100%;
}
.machineTable {
width: 100%;
border-collapse: collapse;
table-layout: auto;
}
.machineTable th,
.machineTable td {
padding: 10px;
text-align: center;
border-bottom: 1px solid #cfcfcf;
word-wrap: break-word;
overflow-wrap: break-word;
}
.machineTable th {
position: sticky;
top: 0px;
background-color: rgb(1, 92, 184);
color: white;
}
.machineTable tr {
min-width: 400px;
}
.disponible {
display: flex;
width: 100%;
height: 100%;
background-color: green;
color: green;
font-weight: bold;
}
.ocupado {
color: red;
font-weight: bold;
}
.actions {
text-align: center;
}
.resetButton {
padding: 8px 16px;
background-color: #ff4d4d;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: bold;
transition: 0.3s;
}
.resetButton:hover {
background-color: #cc0000;
}
+27 -1
View File
@@ -1,8 +1,29 @@
"use client";
import { useState } from "react";
import apiClient from "@/app/lib/apiClient";
import { useEffect, useState } from "react";
export default function RegisterAlta() {
const [listMajor, setListMajor] = useState([]);
const [major, setMajor] = useState("");
useEffect(() => {
const fetchCarreras = async () => {
try {
const response = await apiClient.get("/carrera");
const sortedCarreras = response.data.sort((a: any, b: any) =>
a.carrera.localeCompare(b.carrera)
);
setListMajor(sortedCarreras);
} catch (error) {
console.error("Error al obtener carreras:", error);
}
};
fetchCarreras();
}, []);
return (
<form className="containerAlta gap gridAlta">
<div className="containerForm">
@@ -59,6 +80,11 @@ export default function RegisterAlta() {
<label className="label">Carrera</label>
<select value={major} onChange={(e) => setMajor(e.target.value)}>
<option value="">Selecciona la carrera</option>
{listMajor.map((carrera: any, index) => (
<option key={index} value={carrera.id_carrera}>
{carrera.carrera}
</option>
))}
</select>
</div>
@@ -15,7 +15,7 @@ function BitacoraAlumno() {
return (
<>
<SearchDate />
<div className={styles.tableContainer}>
<div className={styles.tableContainer} style={{marginTop:"1rem"}}>
<table className={styles.machineTable}>
<thead>
<tr>
@@ -33,7 +33,7 @@ function BitacoraEquipo() {
</button>
</div>
</form>
<div className={styles.tableContainer}>
<div className={styles.tableContainer} style={{marginTop:"1rem"}}>
<table className={styles.machineTable}>
<thead>
<tr>
@@ -18,7 +18,7 @@ function BitacoraMesas() {
return (
<>
<SearchDate />
<div className={styles.tableContainer}>
<div className={styles.tableContainer} style={{marginTop:"1rem"}}>
<table className={styles.machineTable}>
<thead>
<tr>
@@ -20,7 +20,7 @@ export default async function Sanciones(props: { student?: Student }) {
Nombre={props.student.nombre}
/>
<TableSancion />
<TableSancion />
</>
)}
</>
@@ -27,30 +27,28 @@ export default function TableSancion() {
const [sanciones, setSanciones] = useState<any>();
const [button, setButton] = useState<boolean>(false);
useEffect(() => {
const getSanciones = async () => {
const response = await axios.get(
""
);
setSanciones(response);
};
getSanciones();
}, [button]);
// useEffect(() => {
// const getSanciones = async () => {
// const response = await axios.get("");
// setSanciones(response);
// };
// getSanciones();
// }, [button]);
const handlebutton = () => {
setButton(!button);
};
return (
<>
<h1>{sanciones}</h1>
<div className={styles.tableContainer}>
<div className={styles.tableContainer} style={{margin:"1rem 0"}}>
<table className={styles.machineTable}>
<thead>
<tr>
<th>Cuenta</th>
<th>Motivo de la sancion</th>
<th>Motivo de la sanción</th>
<th>Duracion (Semanas) </th>
<th>Fecha Sancion</th>
<th>Fecha Sanción</th>
<th>Podra utilizar el servicio hasta</th>
</tr>
</thead>
@@ -61,15 +59,14 @@ export default function TableSancion() {
<form className="containerForm">
<div className="groupInput">
<select>
<option value="">-- Selecciona una sancion --</option>
<option value="">-- Selecciona una sanción --</option>
<option value="sancion 1">No cerrar sesion (Una semana)</option>
</select>
</div>
</form>
<button className="button buttonSearch" onClick={handlebutton}>
Aplicar sancion
</button>
<h1>{button ? <p>desactivado</p> : <p>activado</p>}</h1>
<button className="button buttonSearch" style={{margin:"1rem 0"}} onClick={handlebutton}>
Aplicar sanción
</button>
</>
);
}
+70
View File
@@ -0,0 +1,70 @@
.tableContainer {
overflow-y: auto;
overflow-x: auto;
border-radius: 4px;
border: 1px solid #cfcfcf;
max-height: 430px;
scrollbar-color: rgb(1, 92, 184) rgba(0, 0, 0, 0);
background-color: #f9f9f9;
width: 100%;
}
.machineTable {
width: 100%;
border-collapse: collapse;
table-layout: auto;
}
.machineTable th,
.machineTable td {
padding: 10px;
text-align: center;
border-bottom: 1px solid #cfcfcf;
word-wrap: break-word;
overflow-wrap: break-word;
}
.machineTable th {
position: sticky;
top: 0px;
background-color: rgb(1, 92, 184);
color: white;
}
.machineTable tr {
min-width: 400px;
}
.disponible {
display: flex;
width: 100%;
height: 100%;
background-color: green;
color: green;
font-weight: bold;
}
.ocupado {
color: red;
font-weight: bold;
}
.actions {
text-align: center;
}
.resetButton {
padding: 8px 16px;
background-color: #ff4d4d;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: bold;
transition: 0.3s;
}
.resetButton:hover {
background-color: #cc0000;
}
+11 -10
View File
@@ -14,16 +14,17 @@ export default function Equipos() {
const [Equipo, setEquipo] = useState("");
const [Data, setData] = useState<Data | any>();
const handleSubmit = async () => {
const response = await axios.get(
`${envConfig.apiUrl}/InformacionEquipo/${Equipo}`
);
const handleSubmit = async (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
// const response = await axios.get(
// `${envConfig.apiUrl}/equipo/${Equipo}`
// );
if (!response) {
return;
}
// if (!response) {
// return;
// }
setData(response);
setData("response");
};
return (
@@ -37,7 +38,7 @@ export default function Equipos() {
}}
/>
<button className="button buttonSearch" onClick={handleSubmit}>
<button type="button" className="button buttonSearch" onClick={handleSubmit}>
Buscar
</button>
</div>
@@ -73,7 +74,7 @@ export default function Equipos() {
<option value="4">mmmmm</option>
<option value="5">mmmmm</option>
</select>
<div className="containerButton">
<div className="containerButton" style={{marginTop:"1rem"}}>
<button className="button buttonSearch">Nuevo</button>
<button className="button buttonSearch">Editar</button>
</div>
+63
View File
@@ -0,0 +1,63 @@
import styles from "./Page.module.css";
export default async function TableEquipos() {
return (
<section className="containerSection">
<div className={styles.tableContainer}>
<table className={styles.machineTable}>
<thead>
<tr style={{fontSize:"15px"}}>
<th>Ubicación</th>
<th>Nombre</th>
<th>Plataforma</th>
<th>Área</th>
<th>Activo</th>
</tr>
<tr>
<th>
<select style={{ minWidth: "50px" }}>
<option style={{ minWidth: "50px" }}>pcnet1</option>
</select>
</th>
<th>
<select style={{ minWidth: "50px" }}>
<option style={{ minWidth: "50px" }}>mostrar todos</option>
</select>
</th>
<th>
<select style={{ minWidth: "50px" }}>
<option style={{ minWidth: "50px" }}>windows</option>
</select>
</th>
<th>
<select style={{ minWidth: "50px" }}>
<option style={{ minWidth: "50px" }}>mostrar Todo</option>
</select>
</th>
<th>
<select style={{ minWidth: "50px" }}>
<option style={{ minWidth: "50px" }}>si</option>
</select>
</th>
</tr>
</thead>
<tbody>
{/* {machines.map((machine, index) => (
<tr key={index}>
<td>{machine.ubicacion}</td>
<td>{machine.nombre}</td>
<td>{machine.plataforma}</td>
<td>{machine.area}</td>
<td className={machine.disponible ? "disponible" : ""}>
{machine.disponible ? "si" : "no"}
</td>
</tr>
))} */}
</tbody>
</table>
</div>
</section>
);
}
//IO
@@ -12,7 +12,7 @@ const EnviarMensaje = ({ titulo, opciones }: EnviarMensajeProps) => {
const [mensaje, setMensaje] = useState("");
const [customMsg, setCustomMsg] = useState("");
const handleSubmit = (e: React.FormEvent) => {
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
// Aquí puedes manejar el envío (guardar en estado global, enviar a backend, etc.)
};
@@ -51,6 +51,7 @@ const EnviarMensaje = ({ titulo, opciones }: EnviarMensajeProps) => {
</option>
))}
</select>
<input type="checkbox" style={{minWidth:"20px", width:"20px",maxWidth:"30px"}}/>
</div>
{/* Mensaje personalizado */}
@@ -62,10 +63,12 @@ const EnviarMensaje = ({ titulo, opciones }: EnviarMensajeProps) => {
onChange={(e) => setCustomMsg(e.target.value)}
placeholder="Escribe tu mensaje..."
/>
<input type="checkbox" style={{minWidth:"20px", width:"20px",maxWidth:"30px"}}/>
</div>
{/* Botón de enviar */}
<button className="button buttonSearch" type="submit">
<button className="button buttonSearch" type="submit" style={{marginTop:"1rem"}}>
Mandar mensaje
</button>
</form>
@@ -1,57 +0,0 @@
@keyframes slideInLeft {
0% {
opacity: 0;
transform: translateX(-100%);
}
100% {
opacity: 0.9;
transform: translateX(0);
}
}
@keyframes slideOutRight {
0% {
opacity: 0.9;
transform: translateX(0);
}
100% {
opacity: 0;
transform: translateX(100%);
}
}
.messageBox {
display: flex;
position: absolute;
padding: 0.5rem 1.25rem;
border-radius: 0 4px 4px 0;
font-weight: bold;
font-size: 2rem;
text-align: center;
z-index: 100;
top: 0;
left: 0;
max-width: 500px;
max-height: min-content;
opacity: 0;
}
.messageBox:not(.hidden) {
animation: slideInLeft 0.5s ease forwards;
}
.messageBox.hidden {
animation: slideOutRight 0.5s ease forwards;
}
.success {
background-color: #d1f7c4;
color: #000000;
border: 1px solid #a5d6a7;
}
.error {
background-color: #ffcdd2;
color: #000000;
border: 1px solid #ef9a9a;
}
@@ -1,42 +0,0 @@
"use client";
import { useEffect, useState } from "react";
import "./AlertBox.css";
import ClearParams from "../ClearParams/ClearParams";
interface AlertBoxProps {
message: string | null;
type: "error" | "success";
duration?: number;
}
export default function AlertBox({
message,
type,
duration = 6000,
}: AlertBoxProps) {
const [visible, setVisible] = useState(false);
const [text, setText] = useState("");
useEffect(() => {
if (message) {
setText(message);
setVisible(true);
const timeout = setTimeout(() => {
setVisible(false);
setTimeout(() => setText(""), 500);
}, duration);
return () => clearTimeout(timeout);
}
}, [message, duration]);
if (!text) return null;
return (
<div className={`messageBox ${type} ${!visible ? "hidden" : ""}`}>
{text}
</div>
);
}
@@ -1,28 +0,0 @@
"use client";
import { useEffect } from "react";
interface ClearParamsProps {
paramsToClear: string[];
}
export default function ClearParams({ paramsToClear }: ClearParamsProps) {
useEffect(() => {
if (typeof window === "undefined") return;
const url = new URL(window.location.href);
let changed = false;
paramsToClear.forEach((param) => {
if (url.searchParams.has(param)) {
url.searchParams.delete(param);
changed = true;
}
});
if (changed) {
window.history.replaceState({}, "", url.toString());
}
}, [paramsToClear]);
return null;
}
-40
View File
@@ -1,40 +0,0 @@
"use client";
import { ReactNode, useCallback } from "react";
import { useRouter, usePathname, useSearchParams } from "next/navigation";
interface FormHandlerProps {
children: ReactNode;
onSubmit: () => Promise<void>;
}
export default function FormHandler({ children, onSubmit }: FormHandlerProps) {
const router = useRouter();
const searchParams = useSearchParams();
const pathname = usePathname();
const updateParams = useCallback(
(updates: Record<string, string | null>) => {
const params = new URLSearchParams(searchParams.toString());
Object.entries(updates).forEach(([key, value]) => {
if (value === null) params.delete(key);
else params.set(key, value);
});
router.push(`${pathname}?${params.toString()}`);
},
[searchParams, router, pathname]
);
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
try {
await onSubmit();
} catch (err: any) {
updateParams({ error: String(err) });
}
};
return <form onSubmit={handleSubmit}>{children}</form>;
}
@@ -1,25 +1,25 @@
'use client';
interface InformationProps {
[key: string]: string | number | undefined;
}
export default function Information(props: InformationProps) {
// Obtenemos las entradas (key + value) y filtramos los que sean undefined
const entries = Object.entries(props).filter(([_, value]) => value !== undefined);
const entries = Object.entries(props).filter(
([_, value]) => value !== undefined
);
if (entries.length === 0) return null; // no mostrar nada si no hay datos
if (entries.length === 0) return null;
return (
<div className="information">
<ul>
<ul className="informationList">
{entries.map(([key, value]) => (
<li key={key}>
<b>{key}: </b>{value}
<li key={key} className="informationItem">
<span className="informationKey">{key}</span>
<span className="informationValue">{value}</span>
</li>
))}
</ul>
</div>
);
}
//IO
//IO
@@ -19,11 +19,10 @@ function SearchUser(props: urlProp) {
}
}, [props.value]);
const handleSubmit = (e: React.FormEvent) => {
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const params = new URLSearchParams(searchParams.toString());
if (numAcount) {
params.delete("error");
params.set("numAcount", `${numAcount}`);
router.push(`${pathname}?${params.toString()}`);
}
@@ -57,3 +56,4 @@ function SearchUser(props: urlProp) {
}
export default SearchUser;
//IO
+14
View File
@@ -0,0 +1,14 @@
"use client";
import { useEffect } from "react";
import toast from "react-hot-toast";
export default function ShowError({ message }: { message: string }) {
useEffect(() => {
if (message) {
toast.error(message);
}
}, [message]);
return null;
}
@@ -1,50 +1,49 @@
'use client'
"use client";
import { useState, ReactNode } from "react";
import "./StepNavigator.css"
import "./StepNavigator.css";
interface StepNavigatorProps {
totalSteps: number;
children: ReactNode[];
onFinish?: () => void;
totalSteps: number;
children: ReactNode[];
onFinish?: () => void;
}
export default function StepNavigator({ totalSteps, children, onFinish }: StepNavigatorProps) {
const [step, setStep] = useState(1);
export default function StepNavigator({
totalSteps,
children,
onFinish,
}: StepNavigatorProps) {
const [step, setStep] = useState(1);
const handleNext = (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
if (step < totalSteps) setStep(step + 1);
else if (onFinish) onFinish();
};
const handleNext = (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
if (step < totalSteps) setStep(step + 1);
else if (onFinish) onFinish();
};
const handlePrev = (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
if (step > 1) setStep(step - 1);
};
const handlePrev = (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
if (step > 1) setStep(step - 1);
};
return (
<section className="stepNavigator">
{children[step - 1]}
return (
<section className="stepNavigator">
{children[step - 1]}
<div className="absoluteButton">
{step > 1 && (
<button
onClick={handlePrev}
className="button buttonSearch">
Atrás
</button>
)}
<div className="absoluteButton">
{step > 1 && (
<button onClick={handlePrev} className="button buttonSearch">
Atrás
</button>
)}
{step < totalSteps &&
<button
onClick={handleNext}
className="button buttonSearch"
>
Siguiente
</button>
}
</div>
</section>
);
{step < totalSteps && (
<button onClick={handleNext} className="button buttonSearch">
Siguiente
</button>
)}
</div>
</section>
);
}
+31
View File
@@ -0,0 +1,31 @@
import apiClient from "@/app/lib/apiClient";
import { useEffect, useState } from "react";
export default function selectArea() {
const [area, setArea] = useState([]);
useEffect(() => {
const fetchArea = async () => {
const response = await apiClient.get("/area-ubicacion");
setArea(response.data);
};
fetchArea();
});
return (
<>
<label className="label">Seleccione el área</label>
<select>
{area.map((area) => (
<option>{area}</option>
))}
<option value="0">windows</option>
<option value="1">ADOBE CREATIVE SUITE</option>
<option value="2">mmmmm</option>
<option value="3">mmmmm</option>
<option value="4">mmmmm</option>
<option value="5">mmmmm</option>
</select>
</>
);
}
+8
View File
@@ -0,0 +1,8 @@
.tableContainer {
width: 100%;
max-width: 450px;
overflow-x: hidden;
scrollbar-color: #2563eb #ffffff00;
scroll-behavior: smooth;
transition: all 0.3s ease;
}
+40
View File
@@ -0,0 +1,40 @@
"use client";
import React from "react";
import styles from "./table.module.css";
interface TableProps {
headers: string[];
data: Array<Record<string, any>>;
}
export default function Table({ headers, data }: TableProps) {
return (
<div className={styles.tableContainer}>
<table>
<thead>
<tr>
{headers.map((header, index) => (
<th key={index}>{header}</th>
))}
</tr>
</thead>
<tbody>
{data.length > 0 ? (
data.map((row, rowIndex) => (
<tr key={rowIndex}>
{headers.map((header, colIndex) => (
<td key={colIndex}>{row[header]}</td>
))}
</tr>
))
) : (
<tr>
<td colSpan={headers.length}>No hay registros</td>
</tr>
)}
</tbody>
</table>
</div>
);
}
@@ -1,36 +0,0 @@
"use client";
import { useEffect, useState } from "react";
import AlertBox from "@/app/Components/Global/AlertBox/AlertBox";
import ClearParams from "@/app/Components/Global/ClearParams/ClearParams";
import { useSearchParams } from "next/navigation";
export default function GlobalAlert() {
const searchParams = useSearchParams();
const [showSuccess, setShowSuccess] = useState<string | null>(null);
const [showError, setShowError] = useState<string | null>(null);
useEffect(() => {
const success = searchParams.get("success");
const error = searchParams.get("error");
if (success) setShowSuccess(success);
if (error) setShowError(error);
}, []);
return (
<section className="containerAlerts">
{showError && (
<>
<AlertBox key={Date.now()} message={showError} type="error" />
</>
)}
{showSuccess && (
<>
<AlertBox key={Date.now()} message={showSuccess} type="success" />
</>
)}
</section>
);
}
+11 -15
View File
@@ -4,6 +4,7 @@ import Cookies from "js-cookie";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { PostImpressions } from "@/app/lib/postImpressions";
import toast from "react-hot-toast";
interface CostOption {
value: number;
@@ -27,24 +28,19 @@ function Impressions({ costs, numAcount }: ImpressionsProps) {
};
const handlePayment = async () => {
const currentUrl = new URL(window.location.href);
const params = new URLSearchParams();
const setError = (msg: string) => {
params.set("error", msg);
router.push(`${currentUrl}&${params.toString()}`);
};
if (!numAcount) {
return setError("busca de nuevo al estudiante");
toast.error("busca de nuevo al estudiante");
return;
}
if (!pages) {
return setError("Ingresa el numero de hojas a imprimir");
toast.error("Ingresa el numero de hojas a imprimir");
return;
}
if (!cost) {
return setError("Selecciona un costo");
toast.error("Selecciona un costo");
return;
}
const result = await PostImpressions({
@@ -56,16 +52,16 @@ function Impressions({ costs, numAcount }: ImpressionsProps) {
if (result.error) {
if (result.error === "Token inválido") handleLogout();
else {
return setError(`Error: ${result.error}`);
toast.error(result.error);
return;
}
return;
}
setPages("");
setCost("");
params.delete("error");
params.set("success", "Impresion cobrada correctamente");
router.push(`${currentUrl}&${params.toString()}`);
toast.success("Impresion cobrada correctamente");
router.refresh();
};
return (
@@ -1,5 +1,6 @@
"use client";
import { useState } from "react";
import SelectAreas from "../SelectAreas";
interface DatosEquipo {
titulo: string;
@@ -43,7 +44,7 @@ const caracteristicasPorEquipo: Record<string, string[]> = {
// Agrega más equipos según necesites
};
function ProgramSelector({ titulo, opcion }: DatosEquipo) {
export default function ProgramSelector({ titulo, opcion }: DatosEquipo) {
// Estado del equipo seleccionado
const [equipoSeleccionado, setEquipoSeleccionado] = useState("");
// Estado de los checkboxes (programas)
@@ -108,5 +109,3 @@ function ProgramSelector({ titulo, opcion }: DatosEquipo) {
</form>
);
}
export default ProgramSelector;
@@ -6,7 +6,6 @@
max-height: 430px;
scrollbar-color: rgb(1, 92, 184) rgba(0, 0, 0, 0);
background-color: #f9f9f9;
width: 65%;
}
.machineTable {
@@ -16,15 +16,15 @@ function QuitarSancion() {
const [quitarSanciones, SetQuitarSanciones] = useState<quitarSanciones[]>([]);
return (
<section className="containerSection">
<div className={styles.tableContainer}>
<div className={styles.tableContainer} style={{margin:"1rem 0"}}>
<table className={styles.machineTable}>
<thead>
<tr>
<th>id</th>
<th>Nombre</th>
<th>Motivo Sancion</th>
<th>Motivo Sanción</th>
<th>Duracion (semanas) </th>
<th>Fecha Sancion</th>
<th>Fecha Sanción</th>
<th>Podria utilizar el servicio hasta</th>
</tr>
</thead>
@@ -42,7 +42,7 @@ function QuitarSancion() {
</tbody>
</table>
</div>
<button className="button buttonSearch">Quitar Sancion</button>
<button className="button buttonSearch" style={{marginTop:"1rem"}}>Quitar Sanción</button>
</section>
);
}
+24
View File
@@ -0,0 +1,24 @@
.groupInformation {
display: flex;
max-width: 500px;
min-width: 40px;
width: 100%;
justify-content: end;
gap: 10px;
}
.informationButton {
text-align: center;
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
border: 1px solid #cfcfcf;
background-color: #fff;
max-width: 40px;
min-width: 40px;
}
.informationButton:hover {
border: 1px solid #cfcfcf;
background-color: #cfcfcf;
}
+13 -20
View File
@@ -1,5 +1,6 @@
"use client";
import toast from "react-hot-toast";
import { useState } from "react";
import { PostReceipt } from "@/app/lib/postReceipt";
import { useRouter } from "next/navigation";
@@ -28,28 +29,24 @@ function Receipt({ numAcount }: ReceiptsProps) {
//restrict this month//
const handleSaveReceipt = async () => {
const currentUrl = new URL(window.location.href);
const params = new URLSearchParams();
const setError = (msg: string) => {
params.set("error", msg);
router.push(`${currentUrl}&${params.toString()}`);
};
if (!numAcount) {
return setError("busca de nuevo al estudiante");
toast.error("busca de nuevo al estudiante");
return;
}
if (!folio) {
return setError("Ingresa el folio del ticket");
toast.error("Ingresa el folio del ticket");
return;
}
if (!amount) {
return setError("Coloca el monto a depositar");
toast.error("Coloca el monto a depositar");
return;
}
if (!date) {
return setError("Coloca la fecha");
toast.error("Coloca la fecha");
return;
}
try {
@@ -64,11 +61,10 @@ function Receipt({ numAcount }: ReceiptsProps) {
setAmount("");
setDate("");
params.delete("error");
params.set("success", "Recibo guardado");
router.push(`${currentUrl}&${params.toString()}`);
toast.success("Recibo guardado");
router.refresh();
} catch (err: any) {
setError(String(err));
toast.error(String(err));
}
};
@@ -111,10 +107,7 @@ function Receipt({ numAcount }: ReceiptsProps) {
if (value === "" || numericValue <= 1000) {
setAmount(value);
} else {
const currentUrl = new URL(window.location.href);
const params = new URLSearchParams();
params.set("error", "El monto no puede superar $1000.00");
router.push(`${currentUrl}&${params.toString()}`);
toast.error("El monto no puede superar $1000.00");
}
}
}}
+69
View File
@@ -0,0 +1,69 @@
.tableContainer {
overflow-y: auto;
overflow-x: auto;
border-radius: 4px;
border: 1px solid #cfcfcf;
max-height: 430px;
scrollbar-color: rgb(1, 92, 184) rgba(0, 0, 0, 0);
background-color: #f9f9f9;
width: 100%;
}
.machineTable {
width: 100%;
border-collapse: collapse;
table-layout: auto;
}
.machineTable th,
.machineTable td {
padding: 10px;
text-align: center;
border-bottom: 1px solid #cfcfcf;
word-wrap: break-word;
overflow-wrap: break-word;
}
.machineTable th {
position: sticky;
top: 0px;
background-color: rgb(1, 92, 184);
color: white;
}
.machineTable tr {
min-width: 400px;
}
.disponible {
display: flex;
width: 100%;
height: 100%;
background-color: green;
color: green;
font-weight: bold;
}
.ocupado {
color: red;
font-weight: bold;
}
.actions {
text-align: center;
}
.resetButton {
padding: 8px 16px;
background-color: #ff4d4d;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: bold;
transition: 0.3s;
}
.resetButton:hover {
background-color: #cc0000;
}
+34
View File
@@ -0,0 +1,34 @@
"use client";
import { useState } from "react";
import styles from "./Page.module.css";
interface reportes {
Servicio: "Plotter";
Total: "$1440.00";
}
function PorRecibos() {
const [reportes, SetQuitarSanciones] = useState<reportes[]>([]);
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>
</tr>
))}
</tbody>
</table>
</div>
);
}
export default PorRecibos;
+43
View File
@@ -0,0 +1,43 @@
"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";
}
function PorServicios() {
const [recibos, setRecibos] = useState<servicios[]>([]);
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>
</tr>
))}
</tbody>
</table>
</div>
);
}
export default PorServicios;
@@ -6,7 +6,7 @@ function SearchDateBetween() {
const [startDate, setStartDate] = useState("");
const [endDate, setEndDate] = useState("");
const handleSubmit = (e: React.FormEvent) => {
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
console.log("Fecha inicio:", startDate);
console.log("Fecha fin:", endDate);
+5 -4
View File
@@ -1,11 +1,12 @@
"use client";
export default function SearchEquipo() {
return (
<form action="">
<form className="containerForm">
<label>Numero de equipo</label>
<input type="text" />
<button className="button buttonSearch">Buscar</button>
<div className="groupInput">
<input type="text" placeholder="Coloca el numero de equipo"/>
<button className="button buttonSearch">Buscar</button>
</div>
</form>
);
}
+5 -6
View File
@@ -1,12 +1,11 @@
export default function SearchTable() {
return (
<form className="containerForm">
{" "}
<label>Numero de Mesa</label>
<div className="groupInput">
<input type="text" placeholder="Coloca el numero de Mesa" />
<button className="buttonSearch button">Buscar</button>
</div>
<label>Numero de Mesa</label>
<div className="groupInput">
<input type="text" placeholder="Coloca el numero de Mesa" />
<button className="buttonSearch button">Buscar</button>
</div>
</form>
);
}
+17 -17
View File
@@ -1,35 +1,35 @@
"use clien";
import React, { useEffect, useState } from "react";
"use client";
import axios from "axios";
import { useEffect, useState } from "react";
export default function SelectAreas() {
const [areas, setAreas] = useState([]); // aquí guardaremos los datos del servidor
const [areas, setAreas] = useState([]);
const [selectedArea, setSelectedArea] = useState("");
// useEffect se ejecuta una vez al montar el componente
useEffect(() => {
fetch("https://venus.acatlan.unam.mx/asignacionTiempo_test/area-ubicacion")
.then((response) => response.json())
axios
.get("https://venus.acatlan.unam.mx/asignacionTiempo_test/area-ubicacion")
.then((data) => {
console.log("Áreas obtenidas:", data);
setAreas(data);
setAreas(data.data);
})
.catch((error) => {
console.error("Error al traer las áreas:", error);
});
}, []);
const handleChange = (e) => {
setSelectedArea(e.target.value);
console.log("Área seleccionada:", e.target.value);
};
return (
<div>
<label htmlFor="areaSelect">Selecciona un área:</label>
<select id="areaSelect" value={selectedArea} onChange={handleChange}>
<option value="">-- Selecciona una opción --</option>
{areas.map((area) => (
<select
id="areaSelect"
value={selectedArea}
onChange={(e) => setSelectedArea(e.target.value)}
>
<option value="">-- Selecciona una opción --</option>
{areas.map((area: any, index) => (
<option key={index} value={area.area}>
{area.area}
</option>
))}
</select>
</div>
-1
View File
@@ -3,7 +3,6 @@
gap: 1rem;
justify-content: center;
align-items: center;
margin: 10px;
flex-wrap: wrap;
}
@@ -1,4 +1,5 @@
"use client";
import apiClient from "@/app/lib/apiClient";
import { useState } from "react";
export default function ChangePassword() {
@@ -6,10 +7,11 @@ export default function ChangePassword() {
const [newPass, setNewPass] = useState("");
const [confirmNewPass, setconfirmNewPass] = useState("");
const handleChangePass = (e: React.FormEvent) => {
const handleChangePass = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault;
const data = { pass, newPass, confirmNewPass };
console.log(data);
await apiClient.post("/user/create", data);
};
return (
+14 -9
View File
@@ -3,33 +3,41 @@ import { useState } from "react";
import { loginUser } from "@/app/lib/login";
import { useRouter } from "next/navigation";
import toast from "react-hot-toast";
import "./Login.css";
import AlertBox from "../../Global/AlertBox/AlertBox";
function Login() {
const [user, setUser] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const [alert, setAlert] = useState("");
const router = useRouter();
const handleLogin = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (!user) {
toast.error("Coloca un usuario");
return;
}
if (!password) {
toast.error("Coloca una contraseña");
return;
}
const data = await loginUser(user, password);
if ("error" in data) {
setError(data.error);
toast.error(data.error);
} else {
const token = data.access_token;
const payload = JSON.parse(atob(token.split(".")[1]));
const usuario = payload.usuario;
document.cookie = `token=${token}; path=/; SameSite=Strict`;
document.cookie = `usuario=${usuario}; path=/; SameSite=Strict`;
document.cookie = `user=${usuario}; path=/; SameSite=Strict`;
setAlert("Inicio de sesión exitoso");
toast.success("Inicio de sesión exitoso");
router.push("/Impresiones");
}
};
@@ -70,9 +78,6 @@ function Login() {
>
Iniciar sesión
</button>
{error && <AlertBox message={error} type="error" />}
{alert && <AlertBox message={alert} type="success" />}
</form>
</section>
);
@@ -24,7 +24,7 @@ function BarNavigation() {
<ul className={openMenu ? "active" : ""}>
<li className={`subMenu ${openSubMenu === 0 ? "open" : ""}`}>
<span onClick={() => toggleSubMenu(0)}>Inscripcion</span>
<span onClick={() => toggleSubMenu(0)}>Inscripción</span>
<ul className="containerLinks" onClick={toggleMenu}>
<Link href="/Alta" className="links">
<li>Alta</li>
@@ -33,7 +33,7 @@ function BarNavigation() {
<li>Agregar Tiempo</li>
</Link>
<Link href="/Inscripcion" className="links">
<li>Inscripcion</li>
<li>Inscripción</li>
</Link>
</ul>
</li>
@@ -90,7 +90,7 @@ function BarNavigation() {
</li>
<li className="subMenu" onClick={toggleMenu}>
<Link href="/QuitarSancion" className="links">
<span>Quitar sancion</span>
<span>Quitar sanción</span>
</Link>
</li>
<li className="subMenu" onClick={toggleMenu}>
+45
View File
@@ -0,0 +1,45 @@
"use client";
import { Toaster } from "react-hot-toast";
import React from "react";
export function ToastProvider({ children }: { children: React.ReactNode }) {
return (
<>
{children}
<Toaster
toastOptions={{
style: {
padding: "16px",
fontSize: "16px",
maxWidth: "400px",
},
error: {
duration: 6000,
style: {
fontSize: "1.5rem",
backgroundColor: "#fee2e2dd",
color: "#000000ff",
fontWeight: "600",
padding: "1.5rem",
},
},
success: {
duration: 6000,
style: {
fontSize: "1.5rem",
backgroundColor: "#d1fae5dd",
color: "#000000ff",
fontWeight: "600",
padding: "1.5rem",
},
},
}}
position="top-left"
reverseOrder={false}
/>
</>
);
}