Cambios de estilo

This commit is contained in:
evenegas
2025-06-30 12:55:08 -06:00
parent 6b8d7ba585
commit b18fd46acf
12 changed files with 253 additions and 108 deletions
+1 -1
View File
@@ -57,7 +57,7 @@ export default function Page() {
transition={{ delay: 0, duration: 0.5 }}
>
<h2 className="text-dorado">Bienvenido al sistema de</h2>
<h1 className="text-azul">Carga Masiva</h1>
<h1 className="text-azul">Servicios PCPUMA y CEDETEC</h1>
<SimpleInput
label="Usuario"
className={{
+191
View File
@@ -0,0 +1,191 @@
"use client";
import React, { useState } from "react";
const tiposUsuarioEstudiante = [
"Diplomado", "Extra Largo", "Servicio Social", "Idiomas R (UNAM)",
"Idiomas Sabatino", "Reinscrito", "Posgrado", "Intercambio UNAM",
"Movilidad", "Ampliación de Conocimiento", "Licenciatura",
];
const tiposUsuarioEspecial = ["Profesor", "Trabajadores"];
const FormularioCargaIndividual = () => {
const [tipoUsuario, setTipoUsuario] = useState("");
const [formData, setFormData] = useState<any>({});
const [errores, setErrores] = useState<string[]>([]);
const [mensaje, setMensaje] = useState("");
const camposEstudiante = [
"num_cuenta", "carrera", "clave_carrera", "nombre",
"a_paterno", "a_materno", "fecha_nacimiento", "genero", "generacion",
];
const camposEspecial = [
"num_cuenta", "nombre", "a_paterno", "a_materno", "rfc",
"fecha_nacimiento", "genero", "carrera", "clave_carrera",
];
const camposVisibles =
tipoUsuario === ""
? []
: tiposUsuarioEstudiante.includes(tipoUsuario)
? camposEstudiante
: tiposUsuarioEspecial.includes(tipoUsuario)
? camposEspecial
: [];
const handleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>
) => {
const { name, value } = e.target;
if (name === "tipo_usuario") {
setTipoUsuario(value);
setFormData((prev: any) => ({
...prev,
tipo_usuario: value,
}));
} else {
setFormData((prev: any) => ({
...prev,
[name]: value,
}));
}
};
const validarCampos = (): boolean => {
const faltantes = camposVisibles.filter(
(campo) => !formData[campo] || formData[campo].toString().trim() === ""
);
setErrores(faltantes);
return faltantes.length === 0;
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setMensaje("");
if (!validarCampos()) {
setMensaje("Por favor llena todos los campos obligatorios.");
return;
}
try {
const token = localStorage.getItem("token");
const normalizado = { ...formData };
if (normalizado.fecha_nacimiento) {
normalizado.fecha_nacimiento = normalizado.fecha_nacimiento.replaceAll("-", "").slice(0, 8);
}
if (normalizado.generacion !== undefined && normalizado.generacion !== "") {
const num = Number(normalizado.generacion);
if (!isNaN(num)) {
normalizado.generacion = num;
} else {
delete normalizado.generacion;
}
}
const response = await fetch("http://localhost:4000/excel/carga", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify(normalizado),
});
if (!response.ok) {
const error = await response.json();
console.error("Error del servidor:", error);
setMensaje("Ocurrió un error al guardar el usuario ❌");
return;
}
setMensaje("Usuario guardado correctamente ✅");
setFormData({});
setTipoUsuario("");
} catch (err) {
console.error("Error en petición fetch:", err);
setMensaje("Ocurrió un error al guardar el usuario ❌");
}
};
return (
<form onSubmit={handleSubmit} style={{ maxWidth: "600px", margin: "0 auto" }}>
<h2 className="text-xl font-semibold mt-4 mb-3 text-start">Carga Individual</h2>
{/* Select tipo_usuario */}
<div className="mb-3 text-start">
<label className="form-label fw-bold">Tipo de Usuario</label>
<select
name="tipo_usuario"
className="form-select"
value={tipoUsuario}
onChange={handleChange}
required
>
<option value="">Selecciona una opción</option>
{[...tiposUsuarioEstudiante, ...tiposUsuarioEspecial].map((tipo) => (
<option key={tipo} value={tipo}>
{tipo}
</option>
))}
</select>
</div>
{/* Renderizado dinámico de campos */}
{camposVisibles.map((campo) => (
<div key={campo} className="mb-3 text-start">
<label htmlFor={campo} className="form-label fw-bold">
{campo.replace(/_/g, " ").toUpperCase()}
</label>
{/* campo especial "genero" */}
{campo === "genero" ? (
<select
name="genero"
className={`form-select ${errores.includes(campo) ? "is-invalid" : ""}`}
value={formData.genero || ""}
onChange={handleChange}
required
>
<option value="">Selecciona M o F</option>
<option value="M">M</option>
<option value="F">F</option>
</select>
) : (
<input
type={campo === "fecha_nacimiento" ? "date" : "text"}
name={campo}
className={`form-control ${errores.includes(campo) ? "is-invalid" : ""}`}
value={formData[campo] || ""}
onChange={handleChange}
maxLength={campo === "rfc" ? 13 : undefined}
/>
)}
</div>
))}
{mensaje && (
<div className="alert alert-info text-start" role="alert">
{mensaje}
</div>
)}
<div className="d-grid">
<button
type="submit"
className="btn"
style={{ backgroundColor: "#003d79", color: "#fff" }}
>
Guardar Usuario
</button>
</div>
</form>
);
};
export default FormularioCargaIndividual;
+3 -3
View File
@@ -31,7 +31,7 @@ export default function Dashboard() {
const data = await res.json();
console.log("Respuesta del backend:", data);
const fuentesConvertidas: Fuente[] = data.map((mov: any) => ({
const fuentesConvertidas: Fuente[] = data.move.map((mov: any) => ({
nombre: `${mov.reporte}: ${mov.id_mov}`,
id_mov: mov.id_mov,
fecha: new Date(mov.fecha_mov).toLocaleString("es-MX", {
@@ -40,7 +40,7 @@ export default function Dashboard() {
month: "long",
year: "numeric",
}),
activa: true, // puedes ajustar esto si hay una lógica real
activa: data.button,
}));
setFuentes(fuentesConvertidas);
@@ -111,7 +111,7 @@ export default function Dashboard() {
))}
</div>
<h2 className="text-xl font-semibold mt-6 mb-4">Carga</h2>
<h2 style={{ marginTop: "2rem" }} className="text-xl font-semibold mt-6 mb-4">Carga</h2>
<div>
<CargaArchivo />
</div>
+3 -2
View File
@@ -53,5 +53,6 @@
.floating-icon i {
font-size: 1.25rem;
color: #d59f0f; /* Dorado UNAM */
}
color: #d59f0f;
/* Dorado UNAM */
}
+15 -9
View File
@@ -33,9 +33,11 @@ header div:first-child {
background-color: white;
padding: 10px;
border-radius: 50px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); /* sombra suave */
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
/* sombra suave */
display: flex;
gap: 1rem; /* espacio entre los <a> */
gap: 1rem;
/* espacio entre los <a> */
}
header a {
@@ -80,7 +82,7 @@ p {
}
/* Botón principal */
div > button {
div>button {
background-color: #003d79;
color: white;
padding: 0.7rem 2rem;
@@ -90,14 +92,14 @@ div > button {
cursor: pointer;
}
div > div p {
div>div p {
margin: 0;
font-size: 0.9rem;
color: #888;
}
/* Scroll hint */
body > p:last-of-type {
body>p:last-of-type {
margin-top: 2rem;
font-size: 0.85rem;
color: #aaa;
@@ -112,12 +114,16 @@ body > p:last-of-type {
left: none;
width: 100vw;
height: 100vh;
z-index: -1; /* para que esté detrás de todo */
pointer-events: none; /* no interfiere con el contenido */
z-index: -1;
background-color: #003d79c2;
/* para que esté detrás de todo */
pointer-events: none;
/* no interfiere con el contenido */
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
height: 100%;
overflow-y: auto;
}
.circle {
@@ -130,4 +136,4 @@ body > p:last-of-type {
.invert {
filter: invert(1);
}
}
+3 -6
View File
@@ -11,18 +11,15 @@ export default function LandingBody() {
const router = useRouter();
return (
<motion.main
className="overflow"
initial={{ y: 50, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ delay: 1, duration: 1 }}
>
<IconCircle delay={2} />
<strong>Servicios de soporte técnico PCPUMA</strong>
<p>
Accede a PC PUMA Conecta para reportes especializados, módulos de
soporte físico en campus, servicio de correo institucional y
herramientas de gestión académica.
</p>
<strong>Servicios PCPUMA y CEDETEC</strong>
<div>
<Link
-66
View File
@@ -1,66 +0,0 @@
"use client";
import "./globals.css";
import { motion } from "framer-motion";
import Footer from "@/components/layout/footer";
import LandingBody from "./landing_body";
import { useState } from "react";
import Page from "../(auth)/page";
import Dashboard from "../carga/page";
import Consulta from "../visualizacion/page";
type PageKey = "landing" | "login" | "carga" | "consulta" | "publico";
const Home = () => {
const [page, setPage] = useState<PageKey>("landing");
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 1 }}
className="min-vh-100 d-flex flex-column"
>
{/* <Header onNavClick={setPage} /> */}
<div className="circle-background">
{[150, 300, 450, 600, 900].map((size, i) => (
<motion.div
key={i}
className="circle"
initial={{ scale: 0 }}
animate={{ scale: 3 }}
transition={{
delay: 1 + i * 0.3,
duration: 2,
ease: "easeOut",
}}
style={{
width: size,
height: size,
}}
/>
))}
</div>
<div className="flex-grow-1 d-flex flex-column align-items-center justify-content-center text-center">
{/* renderiza el body según el estado `page` */}
{page === "landing" && <LandingBody />}
{page === "login" && <Page />}
{page === "carga" && <Dashboard />}
{page === "consulta" && <Consulta />}
</div>
{/* <Login />
<contenidoAdmin1 />
<contenidoWorker />
<contenidoPublico /> */}
<Footer />
</motion.div>
);
};
export default Home;
+1 -1
View File
@@ -11,7 +11,7 @@ export default function RootLayout({
}>) {
return (
<html lang="en">
<body className="overflow-hidden">
<body className="overflow">
<BootstrapClient />
<motion.div
initial={{ opacity: 0 }}
+13 -11
View File
@@ -10,8 +10,9 @@ import Dashboard from "./carga/page";
import Consulta from "./visualizacion/page";
import LoginPage from "@/containers/LoginPage";
import { useSession } from "@/hooks/use-session";
import FormularioCargaIndividual from "./alta/alta";
export type PageKey = "landing" | "login" | "carga" | "consulta" | "publico";
export type PageKey = "landing" | "login" | "carga" | "consulta" | "carga individual";
export interface HeaderItem {
label: string;
@@ -30,17 +31,17 @@ const Home = () => {
const items: HeaderItem[] = data?.id
? [
{ label: "Inicio", page: "landing" },
{ label: "Consulta", page: "consulta" },
{ label: "Carga", page: "carga" },
{ label: "Público", page: "publico" },
{ label: "Cerrar sesión", onClick: handleLogout },
]
{ label: "Inicio", page: "landing" },
{ label: "Consulta", page: "consulta" },
{ label: "Carga", page: "carga" },
{ label: "Carga individual", page: "carga individual" },
{ label: "Cerrar sesión", onClick: handleLogout },
]
: [
{ label: "Consulta", page: "consulta" },
{ label: "Inicio", page: "landing" },
{ label: "Login", page: "login" },
];
{ label: "Consulta", page: "consulta" },
{ label: "Inicio", page: "landing" },
{ label: "Login", page: "login" },
];
return (
<>
@@ -51,6 +52,7 @@ const Home = () => {
{page === "login" && <LoginPage />}
{page === "consulta" && <Consulta />}
{page === "carga" && <Dashboard />}
{page === 'carga individual' && <FormularioCargaIndividual />}
</div>
<Footer />
+19 -7
View File
@@ -44,10 +44,10 @@ export default function Consulta() {
<main>
<div>
<div>
<h2 className="text-xl font-semibold mb-4">Consulta de servicios</h2>
<h2 className="text-xl font-semibold mb-4">Servicios habilitados</h2>
<Input
label="No.Cuenta"
label="Número de cuenta o trabajador"
placeholder="Ingresa tu No.Cuenta"
value={value}
className={{
@@ -56,9 +56,19 @@ export default function Consulta() {
onChange={(e) => setValue(e.target.value)}
/>
<Button onClick={handleSubmit}>
{loading ? "Consultando..." : "Consultar"}
</Button>
<button
onClick={handleSubmit}
style={{
backgroundColor: "#003d79",
color: "#fff",
padding: "0.5rem 1rem",
border: "none",
borderRadius: "4px",
cursor: "pointer"
}}
>
{loading ? "Buascando..." : "Buascar"}
</button>
</div>
</div>
@@ -71,16 +81,17 @@ export default function Consulta() {
{resultados.length > 0 && (
<table
style={{
marginTop: "2rem",
background: "#fff",
borderCollapse: "collapse",
width: "100%",
maxWidth: "900px",
borderRadius: "8px",
overflow: "hidden",
overflow: "auto",
boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
}}
>
<thead style={{ backgroundColor: "#2563eb", color: "#fff" }}>
<thead style={{ backgroundColor: "#003d79", color: "#fff" }}>
<tr>
{Object.keys(resultados[0]).map((key) => (
<th
@@ -105,6 +116,7 @@ export default function Consulta() {
style={{
padding: "0.75rem",
borderBottom: "1px solid #eee",
textAlign: "left"
}}
>
{String(val)}
+1 -1
View File
@@ -67,7 +67,7 @@ const CargaArchivo: React.FC = () => {
{errores && (
<div className="">
<div className="flex justify-between items-center mb-2">
<strong className="text-lg">Reporte de Errores</strong>
<h2 className="text-xl font-semibold mt-6 mb-4">Reporte de Errores</h2>
<Button
variant="danger"
onClick={() => setErrores(null)}
+3 -1
View File
@@ -33,6 +33,8 @@ export default function LoginPage() {
setEmail("");
setPassword("");
setError("");
window.location.reload();
// Opcional: mostrar mensaje de éxito
alert("Login exitoso. Ahora puedes acceder a Carga y Visualización.");
@@ -52,7 +54,7 @@ export default function LoginPage() {
transition={{ delay: 0, duration: 0.5 }}
>
<h2 className="text-dorado fw-bold">Bienvenido al sistema de</h2>
<h1 className="text-azul fw-bold">Carga Masiva</h1>
<h1 className="text-azul fw-bold">Servicios PC Puma</h1>
<SimpleInput
label="Usuario"
className={{