Cambios de estilo
This commit is contained in:
@@ -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={{
|
||||
|
||||
@@ -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;
|
||||
@@ -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>
|
||||
|
||||
@@ -53,5 +53,6 @@
|
||||
|
||||
.floating-icon i {
|
||||
font-size: 1.25rem;
|
||||
color: #d59f0f; /* Dorado UNAM */
|
||||
}
|
||||
color: #d59f0f;
|
||||
/* Dorado UNAM */
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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 />
|
||||
|
||||
@@ -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)}
|
||||
|
||||
@@ -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)}
|
||||
|
||||
@@ -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={{
|
||||
|
||||
Reference in New Issue
Block a user