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

This commit is contained in:
2025-09-22 14:49:21 -06:00
16 changed files with 303 additions and 258 deletions
+26 -18
View File
@@ -6,8 +6,14 @@ import Toggle from "@/app/Components/Toggle/Toggle";
import SearchDate from "@/app/Components/SearchDate/SearchDate";
import Information from "@/app/Components/Information/information";
import SearchUser from "@/app/Components/SearchUser/searchUser";
import { GetStudent } from "@/app/lib/getStudent";
export default function Page({ searchParams }: any) {
const params = searchParams;
let student: any = null;
let error: string | null = null;
export default function Page() {
const [usuario_sanciones] = useState([
{
no_cuenta: "425530275 ",
@@ -166,8 +172,26 @@ export default function Page() {
label: "Sanciones",
content: (
<>
<SearchUser urlBase="BitacoraSanciones"/>
<SearchUser urlBase="BitacoraSanciones" />
<Information NoCuenta="id cuenta" Nombre="id nombre" />
<form className="containerForm">
<label className="label">Ubicacion de equipo</label>
<div className="groupInput">
<select
value={ubicacion_equipo}
onChange={(e) => setUbicacionEquipo(e.target.value)}
>
<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}>
<table className={styles.machineTable}>
<thead>
@@ -180,22 +204,6 @@ export default function Page() {
</tr>
</thead>
<tbody>
<form className="containerForm">
<label className="label">Ubicacion de equipo</label>
<div className="groupInput">
<select
value={ubicacion_equipo}
onChange={(e) => setUbicacionEquipo(e.target.value)}
>
<option value="">-- Selecciona un equipo --</option>
<option value="255">Equipo 255</option>
</select>
<button className="button buttonSearch" type="submit">
Asignar
</button>
</div>
</form>
{usuario_sanciones.map((sancion, index) => (
<tr key={index}>
<td>{sancion.no_cuenta}</td>
+5 -5
View File
@@ -4,12 +4,12 @@ import Impressions from "../../Components/Impressions/impressions";
import Toggle from "../../Components/Toggle/Toggle";
import Information from "../../Components/Information/information";
import "@/app/globals.css";
import { GetStudent } from "@/app/lib/getStudent";
import AlertBox from "@/app/Components/AlertBox/AlertBox";
import { GetStudent } from "@/app/lib/getStudent";
import "@/app/globals.css";
export default async function Page({ searchParams }: any) {
const params = await searchParams;
const params = await searchParams;
const numAccount = params.numAccount ? Number(params.numAccount) : null;
let student: any = null;
@@ -26,7 +26,7 @@ export default async function Page({ searchParams }: any) {
return (
<section className="containerSection">
{error && <AlertBox message={error} type="error" />}
{error && <AlertBox key={error} message={error} type="error" />}
<h2 className="title">IMPRESIONES Y PLOTEO</h2>
<SearchUser urlBase="Impresiones" />
@@ -46,7 +46,7 @@ export default async function Page({ searchParams }: any) {
{
key: "1",
label: "Recibo",
content: <Receipt />,
content: <Receipt numAccount={student.id_cuenta}/>,
},
{
key: "2",
+34 -13
View File
@@ -1,4 +1,3 @@
"use client";
import "./inscriptions.css";
import SearchUser from "@/app/Components/SearchUser/searchUser";
@@ -6,25 +5,47 @@ import Information from "@/app/Components/Information/information";
import StepNavigator from "@/app/Components/StepNavigator/StepNavigator";
import Receipt from "@/app/Components/Receipt/Receipt";
import Selection from "@/app/Components/Selection/Selection";
import { GetStudent } from "@/app/lib/getStudent";
export default async function Page({ searchParams }: any) {
const params = await searchParams;
const numAccount = params.numAccount ? Number(params.numAccount) : null;
let student: any = null;
let error: string | null = null;
if (numAccount) {
const result = await GetStudent(numAccount);
if (result.error) {
error = result.error;
} else {
student = result;
}
}
export default function Page() {
return (
<section className="containerSection">
<h2 className="title"> INSCRIPCION </h2>
<SearchUser urlBase="Inscripciones"/>
<SearchUser urlBase="Inscripciones" />
<Information
NoCuenta="idcuanta"
Nombre="juan"
Carrera="carrera"
Credito="credito"
/>
{student ? (
<>
<Information
NoCuenta={student.id_cuenta}
Nombre={student.nombre}
Carrera={student.carrera.carrera}
Credito={student.credito}
/>
<StepNavigator totalSteps={2} onFinish={() => console.log()}>
<Selection />
<Receipt />
</StepNavigator>
<StepNavigator totalSteps={2}>
<Selection />
<Receipt numAccount={student.id_cuenta}/>
</StepNavigator>
</>
) : (
<></>
)}
</section>
);
}
+2 -2
View File
@@ -1,6 +1,6 @@
"use client";
import { useState } from "react";
import styles from "./Page.module.css"; // importamos el css
import styles from "./Page.module.css";
import SearchUser from "@/app/Components/SearchUser/searchUser";
export default function Page() {
@@ -17,7 +17,7 @@ export default function Page() {
return (
<section className="containerSection">
<h2 className="title"> Quitar Sanciones </h2>
<SearchUser urlBase="QuitarSanciones"/>
<SearchUser urlBase="QuitarSancion"/>
<div className={styles.tableContainer}>
<table className={styles.machineTable}>
<thead>
+6 -3
View File
@@ -1,7 +1,8 @@
.messageBox {
display: flex;
position: absolute;
padding: 0.5rem 1.25rem;
border-radius: 4px;
border-radius: 0 4px 4px 0;
font-weight: bold;
font-size: 1.5rem;
text-align: center;
@@ -10,13 +11,15 @@
top: 0;
left: 0;
width: 100%;
max-width: 200px;
max-height: min-content;
opacity: 0.9;
transition: opacity 0.5s ease, transform 0.5s ease;
}
.messageBox.hidden {
opacity: 0;
pointer-events: none;
transition: opacity 1s ease-out;
transition: opacity 1s ease-out,transform 0.5 ease;
}
.success {
@@ -1,5 +1,4 @@
.barNavigation {
position: absolute;
text-align: center;
background-color: rgb(1, 92, 184);
padding: 0 20px;
+16 -33
View File
@@ -1,12 +1,12 @@
"use client";
import { useState } from "react";
import axios from "axios";
import { useRouter } from "next/navigation";
import Cookies from "js-cookie";
import { envConfig } from "@/app/lib/config";
import "./Impressions.css";
import AlertBox from "../AlertBox/AlertBox";
import { PostImpressions } from "@/app/lib/postImpressions";
import "./Impressions.css";
interface CostOption {
value: number;
@@ -16,6 +16,7 @@ interface ImpressionsProps {
costs: CostOption[];
numAccount: number | null;
}
function Impressions({ costs, numAccount }: ImpressionsProps) {
const [pages, setPages] = useState("");
const [cost, setCost] = useState("");
@@ -47,38 +48,20 @@ function Impressions({ costs, numAccount }: ImpressionsProps) {
return;
}
const token = Cookies.get("token");
if (!token) {
handleLogout();
const result = await PostImpressions({
id_cuenta:numAccount,
numero_hojas : parseInt(pages),
monto: parseInt(cost) * parseInt(pages),
});
if (result.error) {
if (result.error === "Token inválido") handleLogout();
else setError(result.error);
return;
}
try {
await axios.post(
`${envConfig.apiUrl}/impressions`,
{
numAccount: numAccount,
pages: parseInt(pages),
cost: parseInt(cost) * parseInt(pages),
},
{
headers: {
Authorization: `Bearer ${token}`,
},
}
);
setPages("");
} catch (error: any) {
const errorMessage =
error.response?.data?.error || "No se encontró el estudiante";
if (errorMessage === "Token inválido") {
handleLogout();
}
setError(errorMessage);
}
setPages("");
setCost("")
};
return (
@@ -119,7 +102,7 @@ function Impressions({ costs, numAccount }: ImpressionsProps) {
</div>
<div className="groupLabel">
<label className="label">Total: {pages && `$${pages}.00`}</label>
<label className="label">Total: {pages && `$${parseInt(cost) * parseInt(pages)}.00`}</label>
</div>
<div className="containerButton">
+12 -14
View File
@@ -4,11 +4,12 @@ import { loginUser } from "@/app/lib/login";
import { useRouter } from "next/navigation";
import "./Login.css";
import AlertBox from "../AlertBox/AlertBox";
function Login() {
const [user, setUser] = useState("");
const [password, setPassword] = useState("");
const [message, setMessage] = useState("");
const [error, setError] = useState("");
const router = useRouter();
@@ -18,10 +19,16 @@ function Login() {
const data = await loginUser(user, password);
if ("error" in data) {
setMessage(data.error);
setError(data.error);
} else {
document.cookie = `token=${data.access_token}; path=/; SameSite=Strict`;
setMessage("Inicio de sesión exitoso");
const token = data.access_token;
const payload = JSON.parse(atob(token.split(".")[1]));
const id_usuario = payload.id;
document.cookie = `token=${token}; path=/; SameSite=Strict`;
document.cookie = `id_usuario=${id_usuario}; path=/; SameSite=Strict`;
setError("Inicio de sesión exitoso");
router.push("/Impresiones");
}
};
@@ -63,16 +70,7 @@ function Login() {
Iniciar sesión
</button>
{message && (
<div
className={`messageBox ${
message.includes("exitoso") ? "success" : "error"
}`}
style={{ marginTop: "10px" }}
>
{message}
</div>
)}
{error && <AlertBox message={error} type="error" />}
</form>
</section>
);
+129 -137
View File
@@ -1,141 +1,133 @@
'use client'
import { useEffect, useState } from "react";
import "./Receipt.css"
import { PostReceipt } from "@/app/lib/receipt";
"use client";
import AlertBox from "../AlertBox/AlertBox";
import { useState } from "react";
import { PostReceipt } from "@/app/lib/postReceipt";
import Cookies from "js-cookie";
function Receipt() {
const [folio, setFolio] = useState('');
const [amount, setAmount] = useState('');
const [date, setDate] = useState('');
import "./Receipt.css";
const [error, setError] = useState('');
const [alert, setAlert] = useState('');
const [showError, setShowError] = useState(false);
const [showAlert, setShowAlert] = useState(false);
//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//
//fadeOut alert and error
useEffect(() => {
if (alert) {
setShowAlert(true);
const timeout = setTimeout(() => {
setShowAlert(false);
setTimeout(() => setAlert(''), 500);
}, 6000);
return () => clearTimeout(timeout);
}
}, [alert]);
useEffect(() => {
if (error) {
setShowError(true);
const timeout = setTimeout(() => {
setShowError(false);
setTimeout(() => setError(''), 500);
}, 6000);
return () => clearTimeout(timeout);
}
}, [error]);
//fadeOut alert and error//
const handleSaveReceipt = ( )=>{
//PostReceipt(data{folio,amount,date})
}
return (
<form
onSubmit={(e) => {
e.preventDefault();
handleSaveReceipt();
}}
>
<div className="gap">
<div className='groupInput'>
<label className='label'>Ticket:</label>
<input
type='text'
value={folio}
onChange={(e) => {
const value = e.target.value;
if (/^\d*$/.test(value)) {
setFolio(value);
}
}}
placeholder='Numero de folio...'
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 {
setAlert('')
setError('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">
Guardar
</button>
{error &&
<div className={`messageBox error ${!showError ? 'hidden' : ''}`}>
{error}
</div>
}
{alert &&
<div className={`messageBox success ${!showAlert ? 'hidden' : ''}`}>
{alert}
</div>
}
</div>
</div>
</form>
)
interface ReceiptsProps {
numAccount: number | null;
}
export default Receipt
//IO
function Receipt({ numAccount }: ReceiptsProps) {
const [folio, setFolio] = useState("");
const [amount, setAmount] = useState("");
const [date, setDate] = useState("");
const [error, setError] = useState("");
const [alert, setAlert] = 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 = () => {
if (!numAccount) {
setError("Error busca denuevo al estudiante");
return;
}
if (!folio) {
setError("Ingresa el folio del tiket");
return;
}
if (!amount) {
setError("coloca el monto a depositar");
return;
}
if (!date) {
setError("coloca la fecha");
return;
}
PostReceipt({
id_cuenta: numAccount,
folio_recibo: folio,
monto: Number(amount),
fecha_recibo: date,
});
};
return (
<form
onSubmit={(e) => {
e.preventDefault();
handleSaveReceipt();
}}
>
<div className="gap">
<div className="groupInput">
<label className="label">Ticket:</label>
<input
type="text"
value={folio}
onChange={(e) => {
const value = e.target.value;
if (/^\d*$/.test(value)) {
setFolio(value);
}
}}
placeholder="Numero de folio..."
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 {
setAlert("");
setError("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">Guardar</button>
{error && <AlertBox message={error} type="error" />}
{alert && <AlertBox message={alert} type="success" />}
</div>
</div>
</form>
);
}
export default Receipt;
//IO
+7 -3
View File
@@ -81,10 +81,10 @@ footer p {
.mainContainer {
position: relative;
display: flex;
flex-direction: column;
display: grid;
grid-template-rows: 40px 1fr;
align-items: center;
justify-content: center;
justify-items: center;
height: 100%;
width: 100%;
}
@@ -439,6 +439,10 @@ table tr {
justify-content: center;
align-items: center;
}
footer p {
font-size: 1.2rem;
}
}
@media (max-height: 960px) {
+23
View File
@@ -0,0 +1,23 @@
import axios from "axios";
import Cookies from "js-cookie";
import { envConfig } from "./config";
const apiClient = axios.create({
baseURL: envConfig.apiUrl,
headers: {
"Content-Type": "application/json",
},
});
apiClient.interceptors.request.use((config) => {
const token = Cookies.get("token");
console.log(token)
if (token) {
if (config.headers && "set" in config.headers) {
config.headers.set("Authorization", `Bearer ${token}`);
}
}
return config;
});
export default apiClient;
+13 -10
View File
@@ -2,14 +2,17 @@ import axios from "axios";
import { envConfig } from "./config";
export async function loginUser(usuario: string, password: string) {
try {
const response = await axios.post(`${envConfig.apiUrl}/user`, { usuario, password });
return response.data;
} catch (error: any) {
const msg =
error.response?.data?.message ||
error.message ||
"Error desconocido en el login";
return { error: msg };
}
try {
const response = await axios.post(`${envConfig.apiUrl}/user`, {
usuario,
password,
});
return response.data;
} catch (error: any) {
const msg =
error.response?.data?.message ||
error.message ||
"Error desconocido en el login";
return { error: msg };
}
}
+14
View File
@@ -0,0 +1,14 @@
import apiClient from "@/app/lib/apiClient";
export async function PostImpressions(data: any) {
try {
const response = await apiClient.post("/operations/impressions", data);
return response.data;
} catch (error: any) {
const msg =
error.response?.data?.error ||
error.message ||
"Error desconocido al cobrar impresión";
return { error: msg };
}
}
+15
View File
@@ -0,0 +1,15 @@
import apiClient from "./apiClient";
export async function PostReceipt(data: any) {
try {
const response = await apiClient.post("/operations/receipt", data);
console.log(response.data);
return response.data;
} catch (error: any) {
const msg =
error.response?.data?.message ||
error.message ||
"Error desconocido al crear recibo";
return { error: msg };
}
}
-18
View File
@@ -1,18 +0,0 @@
import axios from "axios";
import { envConfig } from "./config";
export async function PostReceipt(usuario: string, password: string) {
try {
const response = await axios.post(`${envConfig.apiUrl}/operations`, {
usuario,
password,
});
return response.data;
} catch (error: any) {
const msg =
error.response?.data?.message ||
error.message ||
"Error desconocido en el guardado del Recibo";
return { error: msg };
}
}
+1 -1
View File
@@ -5,7 +5,7 @@
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"start": "source ~/.nvm/nvm.sh && node --version && nvm use 22.10.0 && npm install && next build && next start -p 3356",
"start": "next start",
"lint": "next lint"
},
"dependencies": {