Se agregaró inicio de Google y contraseña
This commit is contained in:
@@ -33,7 +33,7 @@ const FormularioCargaIndividual = () => {
|
||||
|
||||
const camposTrabajador = [
|
||||
"num_cuenta", "nombre", "a_paterno", "a_materno", "rfc",
|
||||
"fecha_nacimiento", "genero", "carrera", "clave_carrera",
|
||||
"fecha_nacimiento", "genero", "clave_carrera",
|
||||
];
|
||||
|
||||
const camposVisibles =
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
export default function OAuthCallback() {
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const token = params.get("token");
|
||||
|
||||
if (token) {
|
||||
// Guardar token en localStorage
|
||||
localStorage.setItem("token", token);
|
||||
|
||||
// Disparar evento para que useSession se actualice
|
||||
window.dispatchEvent(new Event("tokenChanged"));
|
||||
|
||||
// Redirigir al Home
|
||||
router.replace("/");
|
||||
} else {
|
||||
// No hay token: redirigir a login
|
||||
router.replace("/");
|
||||
}
|
||||
}, [router]);
|
||||
|
||||
return <p>Procesando inicio de sesión...</p>;
|
||||
}
|
||||
+69
-42
@@ -10,10 +10,14 @@ import LoginPage from "@/containers/LoginPage";
|
||||
import FormularioCargaIndividual from "./alta/alta";
|
||||
import { useSession } from "@/hooks/use-session";
|
||||
import { useEffect, useState } from "react";
|
||||
import path from "path";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
export type PageKey = "landing" | "login" | "carga" | "consulta" | "carga individual";
|
||||
export type PageKey =
|
||||
| "landing"
|
||||
| "login"
|
||||
| "carga"
|
||||
| "consulta"
|
||||
| "carga individual";
|
||||
|
||||
export interface HeaderItem {
|
||||
label: string;
|
||||
@@ -23,62 +27,81 @@ export interface HeaderItem {
|
||||
|
||||
const Home = () => {
|
||||
const [page, setPage] = useState<PageKey>("landing");
|
||||
const { data: user, loading } = useSession(); // usuario viene de useSession()
|
||||
const [tokenProcessed, setTokenProcessed] = useState(false); // ✅ Esperar token
|
||||
const { data: user, loading } = useSession();
|
||||
const [isCarga, setIsCarga] = useState("Carga");
|
||||
const [saludo, setSaludo] = useState("");
|
||||
|
||||
// 🔹 Capturar token antes de montar la UI
|
||||
useEffect(() => {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const token = params.get("token");
|
||||
|
||||
if (token) {
|
||||
localStorage.setItem("token", token);
|
||||
window.dispatchEvent(new Event("tokenChanged"));
|
||||
|
||||
// Limpiar token de URL
|
||||
const cleanUrl = window.location.origin + window.location.pathname;
|
||||
window.history.replaceState({}, document.title, cleanUrl);
|
||||
}
|
||||
|
||||
setTokenProcessed(true); // ✅ Ya procesamos token
|
||||
}, []);
|
||||
|
||||
// 🔹 Saludo
|
||||
useEffect(() => {
|
||||
const origen = user?.tipo;
|
||||
|
||||
if (origen === "LOAD") {
|
||||
setSaludo("Bienvenido al módulo de carga de datos");
|
||||
} else if (origen === "RED") {
|
||||
setSaludo("Bienvenido usuario de red");
|
||||
} else if (origen === "AT") {
|
||||
setSaludo("Bienvenido usuario de AT");
|
||||
} else if (origen === "CORREO") {
|
||||
setSaludo("Bienvenido usuario de correo");
|
||||
} else if (origen === "SOLICITA") {
|
||||
if (origen === "LOAD") setSaludo("Bienvenido al módulo de carga de datos");
|
||||
else if (origen === "RED") setSaludo("Bienvenido usuario de red");
|
||||
else if (origen === "AT") setSaludo("Bienvenido usuario de AT");
|
||||
else if (origen === "CORREO") setSaludo("Bienvenido usuario de correo");
|
||||
else if (origen === "SOLICITA")
|
||||
setSaludo("Bienvenido usuario de préstamos PC Puma");
|
||||
}
|
||||
}, [user]);
|
||||
|
||||
// 🔹 Label carga/descarga
|
||||
useEffect(() => {
|
||||
if (user?.tipo === "LOAD") {
|
||||
setIsCarga("Carga");
|
||||
} else {
|
||||
setIsCarga("Descarga");
|
||||
}
|
||||
if (user?.tipo === "LOAD") setIsCarga("Carga");
|
||||
else setIsCarga("Descarga");
|
||||
}, [user]);
|
||||
|
||||
// 🔹 Redirigir automáticamente si ya hay sesión
|
||||
useEffect(() => {
|
||||
if (!loading && user && page === "login") setPage("landing");
|
||||
}, [loading, user, page]);
|
||||
|
||||
const handleLogout = () => {
|
||||
localStorage.removeItem("token");
|
||||
window.dispatchEvent(new Event("tokenChanged"));
|
||||
location.reload();
|
||||
};
|
||||
|
||||
const items: HeaderItem[] = user
|
||||
? [
|
||||
{ label: "Inicio", page: "landing" },
|
||||
{ label: "Consulta", page: "consulta" },
|
||||
{ label: isCarga, page: "carga" },
|
||||
...(user.tipo === "LOAD"
|
||||
? [
|
||||
{ label: "Carga individual", page: "carga individual" as PageKey },
|
||||
]
|
||||
: []),
|
||||
{ label: "Cerrar sesión", onClick: handleLogout },
|
||||
]
|
||||
{ label: "Inicio", page: "landing" },
|
||||
{ label: "Consulta", page: "consulta" },
|
||||
{ label: isCarga, page: "carga" },
|
||||
...(user.tipo === "LOAD"
|
||||
? [{ label: "Carga individual", page: "carga individual" as PageKey }]
|
||||
: []),
|
||||
{ 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" },
|
||||
];
|
||||
|
||||
// 🔹 Esperamos hasta que se procese el token
|
||||
if (!tokenProcessed) {
|
||||
return <p>Cargando sesión...</p>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header items={items} onClickItem={setPage} />
|
||||
|
||||
{/* Saludo en la esquina superior derecha */}
|
||||
{saludo && (
|
||||
<motion.div
|
||||
className="w-full flex justify-end pr-6 mb-5"
|
||||
@@ -88,17 +111,21 @@ const Home = () => {
|
||||
>
|
||||
<h2 className="text-dorado text-sm text-right">{saludo}</h2>
|
||||
</motion.div>
|
||||
|
||||
|
||||
)}
|
||||
|
||||
<div className="flex-grow-1 d-flex flex-column align-items-center justify-content-center text-center">
|
||||
{page === "landing" && <LandingBody />}
|
||||
{page === "login" && <LoginPage />}
|
||||
{page === "consulta" && <Consulta />}
|
||||
{page === "carga" && <Dashboard />}
|
||||
{page === "carga individual" && user?.tipo === "LOAD" && (
|
||||
<FormularioCargaIndividual />
|
||||
{loading && <p>Cargando sesión...</p>}
|
||||
|
||||
{!loading && (
|
||||
<>
|
||||
{page === "landing" && <LandingBody />}
|
||||
{page === "login" && <LoginPage />}
|
||||
{page === "consulta" && <Consulta />}
|
||||
{page === "carga" && <Dashboard />}
|
||||
{page === "carga individual" && user?.tipo === "LOAD" && (
|
||||
<FormularioCargaIndividual />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
"use client";
|
||||
|
||||
import SimpleInput from "@/components/input";
|
||||
import "../landing/globals.css";
|
||||
import Footer from "@/components/layout/footer";
|
||||
import { motion } from "framer-motion";
|
||||
import PasswordInput from "@/components/password";
|
||||
import Button from "@/components/button";
|
||||
import { useEffect, useState } from "react";
|
||||
import axios from "axios";
|
||||
|
||||
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
|
||||
|
||||
const SetPasswordPage = () => {
|
||||
const [email, setEmail] = useState(""); // email desde URL
|
||||
const [password, setPassword] = useState("");
|
||||
const [confirmPassword, setConfirmPassword] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
// Captura email desde query param
|
||||
useEffect(() => {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const emailParam = params.get("email");
|
||||
if (emailParam) setEmail(emailParam);
|
||||
}, []);
|
||||
|
||||
const handleSetPassword = async () => {
|
||||
if (!password || !confirmPassword) {
|
||||
setError("Por favor ingresa la contraseña y confírmala");
|
||||
return;
|
||||
}
|
||||
if (password !== confirmPassword) {
|
||||
setError("Las contraseñas no coinciden");
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
setError("");
|
||||
|
||||
try {
|
||||
const response = await axios.post(`${apiUrl}/set-password`, {
|
||||
email,
|
||||
password,
|
||||
});
|
||||
|
||||
const token = response.data.access_token;
|
||||
localStorage.setItem("token", token);
|
||||
window.dispatchEvent(new Event("tokenChanged"));
|
||||
|
||||
// Redirigir al Home
|
||||
window.location.href = "/";
|
||||
} catch (err: any) {
|
||||
console.error(err);
|
||||
setError(
|
||||
err.response?.data?.message || "Error al establecer la contraseña"
|
||||
);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<motion.div
|
||||
className="w-full flex justify-end pr-6 mb-5"
|
||||
initial={{ x: -100, opacity: 0 }}
|
||||
animate={{ x: 0, opacity: 1 }}
|
||||
transition={{ duration: 1.5, ease: "easeOut" }}
|
||||
>
|
||||
|
||||
</motion.div>
|
||||
|
||||
<div className="flex-grow-1 d-flex flex-column align-items-center justify-content-center text-center">
|
||||
<motion.main
|
||||
className="auth-page flex-fill flex-grow-1 d-flex flex-column justify-content-center align-items-center"
|
||||
initial={{ y: 50, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
transition={{ delay: 0, duration: 0.5 }}
|
||||
>
|
||||
|
||||
<h1 className="text-dorado">Establece tu contraseña para iniciar sesión</h1>
|
||||
|
||||
<SimpleInput
|
||||
label="Usuario"
|
||||
className={{
|
||||
container: "w-300px mb-3",
|
||||
}}
|
||||
value={email}
|
||||
readOnly
|
||||
/>
|
||||
|
||||
<PasswordInput
|
||||
label="Nueva contraseña"
|
||||
className={{
|
||||
container: "w-300px mb-3",
|
||||
input: "form-control",
|
||||
button: "btn btn-outline-secondary",
|
||||
}}
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
/>
|
||||
|
||||
<PasswordInput
|
||||
label="Confirmar contraseña"
|
||||
className={{
|
||||
container: "w-300px mb-3",
|
||||
input: "form-control",
|
||||
button: "btn btn-outline-secondary",
|
||||
}}
|
||||
value={confirmPassword}
|
||||
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||
/>
|
||||
|
||||
{error && <p className="text-danger text-center mb-3">{error}</p>}
|
||||
|
||||
<div className="d-flex flex-column gap-3 w-300px">
|
||||
<Button
|
||||
onClick={handleSetPassword}
|
||||
disabled={loading}
|
||||
variant="azul"
|
||||
>
|
||||
{loading ? "Guardando..." : "Establecer contraseña"}
|
||||
</Button>
|
||||
</div>
|
||||
</motion.main>
|
||||
</div>
|
||||
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SetPasswordPage;
|
||||
@@ -10,7 +10,6 @@ import { motion } from "framer-motion";
|
||||
|
||||
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
|
||||
|
||||
|
||||
export default function Page() {
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
@@ -21,14 +20,11 @@ export default function Page() {
|
||||
const handleLogin = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
|
||||
const response = await axios.post(`${apiUrl}/login`, {
|
||||
email: email,
|
||||
password: password,
|
||||
});
|
||||
|
||||
|
||||
|
||||
const token = response.data.access_token;
|
||||
localStorage.setItem("token", token);
|
||||
|
||||
@@ -40,8 +36,6 @@ export default function Page() {
|
||||
setPassword("");
|
||||
setError("");
|
||||
window.location.reload();
|
||||
|
||||
// Redirigir al usuario
|
||||
} catch (err: any) {
|
||||
const message =
|
||||
"credenciales incorrectas, por favor verifica tu usuario y contraseña";
|
||||
@@ -51,6 +45,18 @@ export default function Page() {
|
||||
}
|
||||
};
|
||||
|
||||
// 🔑 Captura Enter en cualquier input
|
||||
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === "Enter") {
|
||||
handleLogin();
|
||||
}
|
||||
};
|
||||
|
||||
// 👉 Iniciar sesión con Google
|
||||
const handleGoogleLogin = () => {
|
||||
window.location.href = `${apiUrl}/google`;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<motion.main
|
||||
@@ -69,6 +75,7 @@ export default function Page() {
|
||||
}}
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
|
||||
<PasswordInput
|
||||
@@ -80,14 +87,30 @@ export default function Page() {
|
||||
}}
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
|
||||
{error && <p className="text-danger text-center mb-3">{error}</p>}
|
||||
|
||||
<div className="d-flex gap-3">
|
||||
<Button onClick={handleLogin} disabled={loading} variant="azul" >
|
||||
<div className="d-flex flex-column gap-3 w-300px">
|
||||
{/* Botón normal */}
|
||||
<Button onClick={handleLogin} disabled={loading} variant="azul">
|
||||
{loading ? "Iniciando sesión..." : "Iniciar Sesión"}
|
||||
</Button>
|
||||
|
||||
{/* Botón Google */}
|
||||
<Button
|
||||
onClick={handleGoogleLogin}
|
||||
variant="rojo"
|
||||
className="d-flex align-items-center justify-content-center gap-2"
|
||||
>
|
||||
<img
|
||||
src="https://www.svgrepo.com/show/355037/google.svg"
|
||||
alt="Google"
|
||||
className="w-5 h-5"
|
||||
/>
|
||||
Iniciar sesión con Google
|
||||
</Button>
|
||||
</div>
|
||||
</motion.main>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user