parent
d3c427d01b
commit
8e8f6ac5c4
File diff suppressed because it is too large
Load Diff
@ -1,38 +1,46 @@ |
|||||||
.App { |
*{ |
||||||
text-align: center; |
|
||||||
} |
padding: 0; |
||||||
|
margin: 0; |
||||||
|
box-sizing: border-box; |
||||||
|
|
||||||
.App-logo { |
|
||||||
height: 40vmin; |
|
||||||
pointer-events: none; |
|
||||||
} |
} |
||||||
|
|
||||||
@media (prefers-reduced-motion: no-preference) { |
html, body{ |
||||||
.App-logo { |
background-color: white; |
||||||
animation: App-logo-spin infinite 20s linear; |
|
||||||
} |
} |
||||||
|
|
||||||
|
.App { |
||||||
|
position: relative; |
||||||
|
height: 100vh; |
||||||
|
width: 100vw; |
||||||
|
background-image: url(${ImagenFondo}); |
||||||
|
background-size: cover; |
||||||
|
background-position: center; |
||||||
|
background-repeat: no-repeat; |
||||||
} |
} |
||||||
|
|
||||||
.App-header { |
|
||||||
background-color: #282c34; |
|
||||||
min-height: 100vh; |
.Formulario{ |
||||||
|
height: 550px; |
||||||
|
min-width: 600px; |
||||||
display:flex; |
display:flex; |
||||||
|
flex-wrap: wrap; |
||||||
flex-direction: column; |
flex-direction: column; |
||||||
align-items: center; |
align-items: center; |
||||||
justify-content: center; |
justify-content: center; |
||||||
font-size: calc(10px + 2vmin); |
|
||||||
color: white; |
|
||||||
} |
} |
||||||
|
|
||||||
.App-link { |
|
||||||
color: #61dafb; |
|
||||||
} |
|
||||||
|
|
||||||
@keyframes App-logo-spin { |
|
||||||
from { |
.Home{ |
||||||
transform: rotate(0deg); |
height: 550px; |
||||||
} |
min-width: 600px; |
||||||
to { |
display:flex; |
||||||
transform: rotate(360deg); |
flex-wrap: wrap; |
||||||
} |
flex-direction: column; |
||||||
|
align-items: center; |
||||||
|
justify-content: center; |
||||||
} |
} |
||||||
|
|
||||||
|
@ -1,26 +1,136 @@ |
|||||||
import React from 'react'; |
import React, { useEffect, useState } from 'react'; |
||||||
import logo from './logo.svg'; |
import { BrowserRouter as Router, Route, Routes, Navigate } from 'react-router-dom'; |
||||||
import './App.css'; |
import axios from 'axios'; |
||||||
|
import DTOLogin from './DTos/DTOLogin'; |
||||||
|
import DTOLoginReset from './DTos/DTOLoginReset'; |
||||||
|
import Home from './Componentes/Home'; |
||||||
|
import Formulario from './Componentes/Formulario'; |
||||||
|
import AmazonInvoice from './Componentes/AmazonInvoice'; |
||||||
|
import ImagenLogo from './Imagenes/descarga.png'; |
||||||
|
import ImagenFondo from './Imagenes/fondo.jpg'; |
||||||
|
|
||||||
|
axios.interceptors.response.use( |
||||||
|
function (response) { |
||||||
|
const newToken = response.data.token; |
||||||
|
if (newToken) { |
||||||
|
localStorage.setItem('jwtToken', newToken); |
||||||
|
axios.defaults.headers.common['Authorization'] = `Bearer ${newToken}`; |
||||||
|
} |
||||||
|
return response; |
||||||
|
}, |
||||||
|
function (error) { |
||||||
|
if (error.response && error.response.status === 401) { |
||||||
|
window.location.href = '/login'; |
||||||
|
} |
||||||
|
return Promise.reject(error); |
||||||
|
} |
||||||
|
); |
||||||
|
|
||||||
|
const App: React.FC = () => { |
||||||
|
const [user, setUser] = useState<any>(null); |
||||||
|
const [isLoading, setIsLoading] = useState<boolean>(false); |
||||||
|
|
||||||
|
const handleLogin = async (Usuario: string, Contrasena: string) => { |
||||||
|
setIsLoading(true); |
||||||
|
try { |
||||||
|
if (!Usuario || !Contrasena) { |
||||||
|
alert('Ingrese un usuario y contraseña válidos'); |
||||||
|
setIsLoading(false); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
const data: DTOLogin = { usuario: Usuario, contrasena: Contrasena }; |
||||||
|
const response = await axios.post("https://localhost:5051/api/Usuario/Loging", data); |
||||||
|
if (response.status === 200) { |
||||||
|
setUser(response.data); |
||||||
|
alert('Inicio de sesión correcto'); |
||||||
|
localStorage.setItem('jwtToken', response.data.token); |
||||||
|
} |
||||||
|
} catch (error) { |
||||||
|
console.log(error); |
||||||
|
} finally { |
||||||
|
setIsLoading(false); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
const checkTokenValidity = async () => { |
||||||
|
const token = localStorage.getItem('jwtToken'); |
||||||
|
if (token) { |
||||||
|
try { |
||||||
|
const response = await axios.get('https://localhost:5051/api/Usuario/Validate', { |
||||||
|
headers: { |
||||||
|
Authorization: `Bearer ${token}`, |
||||||
|
}, |
||||||
|
}); |
||||||
|
if (response.status === 200) { |
||||||
|
setUser(response.data); |
||||||
|
} |
||||||
|
} catch (error) { |
||||||
|
console.log(error); |
||||||
|
handleLogout(); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
checkTokenValidity(); |
||||||
|
}, []); |
||||||
|
|
||||||
|
const handleLogout = () => { |
||||||
|
setUser(null); |
||||||
|
localStorage.removeItem('jwtToken'); |
||||||
|
}; |
||||||
|
|
||||||
|
const handlePasswordReset = async (Usuario: string, Contrasena: string, NuevaContrasena: string) => { |
||||||
|
setIsLoading(true); |
||||||
|
try { |
||||||
|
const data1: DTOLoginReset = { usuario: Usuario, contrasena: Contrasena, nuevacontrasena: NuevaContrasena }; |
||||||
|
const response = await axios.put("https://localhost:5051/api/Usuario/ResetPassword", data1); |
||||||
|
if (response.status === 200) { |
||||||
|
alert('Contraseña restablecida correctamente'); |
||||||
|
} |
||||||
|
} catch (error) { |
||||||
|
console.log(error); |
||||||
|
alert('Se ha producido un error al restablecer la contraseña.'); |
||||||
|
} finally { |
||||||
|
setIsLoading(false); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
const handleLoginFormSubmit = (userObject: any) => { |
||||||
|
handleLogin(userObject.usuario, userObject.contrasena); |
||||||
|
}; |
||||||
|
|
||||||
function App() { |
|
||||||
return ( |
return ( |
||||||
<div className="App"> |
<div className='App' style={{ backgroundImage: `url(${ImagenFondo})`, backgroundSize: 'cover', backgroundPosition: 'center', backgroundRepeat: 'no-repeat', height: '100vh' }}> |
||||||
<header className="App-header"> |
<Router> |
||||||
<img src={logo} className="App-logo" alt="logo" /> |
{isLoading ? ( |
||||||
<p> |
<p>Loading...</p> |
||||||
Edit <code>src/App.tsx</code> and save to reload. |
) : ( |
||||||
</p> |
<Routes> |
||||||
<a |
<Route |
||||||
className="App-link" |
path='/login' |
||||||
href="https://reactjs.org" |
element={ |
||||||
target="_blank" |
!user ? ( |
||||||
rel="noopener noreferrer" |
<Formulario handleLogin={handleLoginFormSubmit} handlePasswordReset={handlePasswordReset} /> |
||||||
> |
) : ( |
||||||
Learn React |
<Navigate to='/home' /> |
||||||
</a> |
) |
||||||
</header> |
} |
||||||
|
/> |
||||||
|
<Route path='/home' element={user ? <Home user={user} handleLogout={handleLogout} /> : <Navigate to='/login' />} /> |
||||||
|
<Route path='/amazon-invoice' element={user ? <AmazonInvoice /> : <Navigate to='/login' />} /> |
||||||
|
<Route path='/*' element={<Navigate to='/login' />} /> |
||||||
|
</Routes> |
||||||
|
)} |
||||||
|
{!user && ( |
||||||
|
<div className='logo'> |
||||||
|
<img className='ILogo' src={ImagenLogo} alt='Logo de Imagem' /> |
||||||
|
</div> |
||||||
|
)} |
||||||
|
</Router> |
||||||
</div> |
</div> |
||||||
); |
); |
||||||
} |
}; |
||||||
|
|
||||||
export default App; |
export default App; |
||||||
|
@ -0,0 +1,12 @@ |
|||||||
|
import React from 'react'; |
||||||
|
|
||||||
|
const AmazonInvoice: React.FC = () => { |
||||||
|
return ( |
||||||
|
<div> |
||||||
|
<h1>Bienvenidos a Amazon Invoiceee</h1> |
||||||
|
|
||||||
|
</div> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
export default AmazonInvoice; |
@ -0,0 +1,86 @@ |
|||||||
|
import '../HojasDeEstilo/Formulario.css'; |
||||||
|
import { useState } from 'react'; |
||||||
|
|
||||||
|
interface FormularioProps { |
||||||
|
handleLogin: (userObject: any) => void; |
||||||
|
handlePasswordReset: (usuario: string, contrasena: string, nuevaContrasena: string) => void; |
||||||
|
} |
||||||
|
|
||||||
|
function Formulario({ handleLogin, handlePasswordReset }: FormularioProps) { |
||||||
|
const [usuario, setUsuario] = useState(''); |
||||||
|
const [contrasena, setContrasena] = useState(''); |
||||||
|
const [resetMode, setResetMode] = useState(false); |
||||||
|
const [resetError, setResetError] = useState(false); |
||||||
|
const [nuevaContrasena, setNuevaContrasena] = useState(''); |
||||||
|
|
||||||
|
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => { |
||||||
|
e.preventDefault(); |
||||||
|
|
||||||
|
if (resetMode) { |
||||||
|
if (usuario === '' || contrasena === '' || nuevaContrasena === '') { |
||||||
|
setResetError(true); |
||||||
|
alert('Todos los campos son obligatorios'); |
||||||
|
return; |
||||||
|
} |
||||||
|
setResetError(false); |
||||||
|
handlePasswordReset(usuario, contrasena, nuevaContrasena); |
||||||
|
} else { |
||||||
|
if (usuario === '' || contrasena === '') { |
||||||
|
alert('Todos los campos son obligatorios'); |
||||||
|
return; |
||||||
|
} |
||||||
|
const userObject = { |
||||||
|
usuario, |
||||||
|
contrasena |
||||||
|
}; |
||||||
|
|
||||||
|
handleLogin(userObject); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
return ( |
||||||
|
<section> |
||||||
|
<form className='Formulario' onSubmit={handleSubmit}> |
||||||
|
<h2>Usuario</h2> |
||||||
|
<input |
||||||
|
type='text' |
||||||
|
id='usuario-input' // ID para el cuadro de texto del usuario
|
||||||
|
value={usuario} |
||||||
|
onChange={(e) => setUsuario(e.target.value)} |
||||||
|
/> |
||||||
|
<h3>Contraseña</h3> |
||||||
|
<input |
||||||
|
type='password' |
||||||
|
id='contrasena-input' // ID para el cuadro de texto de la contraseña
|
||||||
|
value={contrasena} |
||||||
|
onChange={(e) => setContrasena(e.target.value)} |
||||||
|
/> |
||||||
|
{resetMode && ( |
||||||
|
<> |
||||||
|
<h3>Nueva Contraseña</h3> |
||||||
|
<input |
||||||
|
type='password' |
||||||
|
id='nuevaContrasena-input' // ID para el cuadro de texto de la nueva contraseña
|
||||||
|
value={nuevaContrasena} |
||||||
|
onChange={(e) => setNuevaContrasena(e.target.value)} |
||||||
|
/> |
||||||
|
<button type="submit" id='submit-reset-button' className='primary'> |
||||||
|
Restablecer Contraseña |
||||||
|
</button> |
||||||
|
</> |
||||||
|
)} |
||||||
|
{!resetMode && ( |
||||||
|
<button type="submit" id='submit-login-button' className='primary'> |
||||||
|
Iniciar Sesión |
||||||
|
</button> |
||||||
|
)} |
||||||
|
</form> |
||||||
|
<button onClick={() => setResetMode(!resetMode)} id='reset-button'> |
||||||
|
{resetMode ? 'Cancelar Restablecer Contraseña' : 'Restablecer Contraseña'} |
||||||
|
</button> |
||||||
|
{resetError && <p>Todos los campos son obligatorios para restablecer la contraseña</p>} |
||||||
|
</section> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
export default Formulario; |
@ -0,0 +1,29 @@ |
|||||||
|
|
||||||
|
import React from 'react'; |
||||||
|
import { Navbar, Nav } from 'react-bootstrap'; |
||||||
|
import { Link } from 'react-router-dom'; |
||||||
|
import '../HojasDeEstilo/Navbar.css' |
||||||
|
|
||||||
|
interface HomeProps { |
||||||
|
user: any; |
||||||
|
handleLogout: () => void; |
||||||
|
} |
||||||
|
|
||||||
|
const Home: React.FC<HomeProps> = ({ user, handleLogout }) => { |
||||||
|
return ( |
||||||
|
<div className="home-container"> |
||||||
|
<h1>BIENVENIDOS AL HOME</h1> |
||||||
|
<Navbar bg="dark" variant="dark" className="navbar-custom"> |
||||||
|
<Nav.Link href="#amazon">No Le Click</Nav.Link> |
||||||
|
<Nav className="ml-auto"> |
||||||
|
<Nav.Link as={Link} to="/amazon-invoice"> |
||||||
|
Amazon Invoice |
||||||
|
</Nav.Link> |
||||||
|
</Nav> |
||||||
|
<Nav.Link onClick={handleLogout}>Cerrar Sesión</Nav.Link> |
||||||
|
</Navbar> |
||||||
|
</div> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
export default Home; |
@ -0,0 +1,4 @@ |
|||||||
|
declare module 'jsonwebtoken' { |
||||||
|
export function decode(token: string): any; |
||||||
|
} |
||||||
|
|
@ -0,0 +1,38 @@ |
|||||||
|
import axios from 'axios'; |
||||||
|
|
||||||
|
axios.interceptors.response.use( |
||||||
|
function (response) { |
||||||
|
const newToken = response.data.token; |
||||||
|
if (newToken) { |
||||||
|
localStorage.setItem('jwtToken', newToken); // Guardar el token en el localStorage
|
||||||
|
} |
||||||
|
return response; |
||||||
|
}, |
||||||
|
function (error) { |
||||||
|
// Manejar el error aquí
|
||||||
|
if (error.response && error.response.status === 401) { |
||||||
|
// Si el estado de respuesta es 401 (No autorizado), redirigir al formulario de inicio de sesión
|
||||||
|
window.location.href = '/login'; |
||||||
|
} |
||||||
|
return Promise.reject(error); |
||||||
|
} |
||||||
|
); |
||||||
|
|
||||||
|
|
||||||
|
// axios.interceptors.response.use(
|
||||||
|
// function (response) {
|
||||||
|
// const newToken = response.data.token;
|
||||||
|
// if (newToken) {
|
||||||
|
// localStorage.setItem('jwtToken', newToken);
|
||||||
|
// }
|
||||||
|
// return response;
|
||||||
|
// },
|
||||||
|
// function (error) {
|
||||||
|
// // Manejar el error aquí
|
||||||
|
// if (error.response && error.response.status === 401) {
|
||||||
|
// // Si el estado de respuesta es 401 (No autorizado), redirigir al formulario de inicio de sesión
|
||||||
|
// window.location.href = '/login';
|
||||||
|
// }
|
||||||
|
// return Promise.reject(error);
|
||||||
|
// }
|
||||||
|
// );
|
@ -0,0 +1,5 @@ |
|||||||
|
export default interface DTOLogin { |
||||||
|
usuario: string, |
||||||
|
contrasena: string,
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
export default interface DTOLoginReset { |
||||||
|
usuario: string, |
||||||
|
contrasena: string,
|
||||||
|
nuevacontrasena: string |
||||||
|
} |
@ -0,0 +1,113 @@ |
|||||||
|
* { |
||||||
|
padding: 0; |
||||||
|
margin: 0; |
||||||
|
box-sizing: border-box; |
||||||
|
} |
||||||
|
|
||||||
|
.contenedor { |
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
align-items: center; |
||||||
|
justify-content: center; |
||||||
|
height: 100vh; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.logo { |
||||||
|
height: 100px; |
||||||
|
min-width: 600px; |
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
align-items: center; |
||||||
|
justify-content: center; |
||||||
|
margin-bottom: 20px; /* Añade un margen inferior para separar el logotipo de los campos de entrada */ |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.Formulario { |
||||||
|
position: relative; |
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
align-items: center; |
||||||
|
justify-content: center; |
||||||
|
gap: 0rem; |
||||||
|
padding: 1rem; |
||||||
|
border-radius: 1rem; |
||||||
|
margin-top: 5px; |
||||||
|
} |
||||||
|
|
||||||
|
h2 { |
||||||
|
display: flex; |
||||||
|
justify-content: center; |
||||||
|
align-items: center; |
||||||
|
flex-direction: column; |
||||||
|
gap: 1rem; |
||||||
|
font-size: 1rem; |
||||||
|
color: white; |
||||||
|
margin-top: 2rem; /* Ajusta el valor según tu necesidad */ |
||||||
|
} |
||||||
|
|
||||||
|
h3 { |
||||||
|
display: flex; |
||||||
|
justify-content: center; |
||||||
|
align-items: center; |
||||||
|
flex-direction: column; |
||||||
|
gap: 1rem; |
||||||
|
font-size: 1rem; |
||||||
|
color: white; |
||||||
|
} |
||||||
|
|
||||||
|
input { |
||||||
|
padding: 12px 20px; |
||||||
|
margin: 8px 0; |
||||||
|
box-sizing: border-box; |
||||||
|
border: 2px solid #ccc; |
||||||
|
border-radius: 4px; |
||||||
|
font-size: 16px; |
||||||
|
font-family: Lato, sans-serif; |
||||||
|
height: 50px; |
||||||
|
} |
||||||
|
|
||||||
|
#submit-login-button { |
||||||
|
background-color: greenyellow; |
||||||
|
border: none; |
||||||
|
color: #000000; |
||||||
|
padding: 12px 24px; |
||||||
|
text-align: center; |
||||||
|
text-decoration: none; |
||||||
|
display: inline-block; |
||||||
|
font-size: 16px; |
||||||
|
margin: 14px 0px; |
||||||
|
cursor: pointer; |
||||||
|
border-radius: 8px; |
||||||
|
transition: background-color 0.3s ease; |
||||||
|
height: 10%; |
||||||
|
width: 237.5px; |
||||||
|
} |
||||||
|
|
||||||
|
button { |
||||||
|
background-color: rgb(132, 130, 238); |
||||||
|
border: none; |
||||||
|
color: white; |
||||||
|
padding: 12px 24px; |
||||||
|
text-align: center; |
||||||
|
text-decoration: none; |
||||||
|
display: inline-block; |
||||||
|
font-size: 16px; |
||||||
|
margin: 14px 0px; |
||||||
|
cursor: pointer; |
||||||
|
border-radius: 8px; |
||||||
|
transition: background-color 0.3s ease; |
||||||
|
height: 10%; |
||||||
|
width: 237.5px; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,44 @@ |
|||||||
|
*{ |
||||||
|
|
||||||
|
padding: 0; |
||||||
|
margin: 0; |
||||||
|
box-sizing: border-box; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
.Home{ |
||||||
|
display: flex; |
||||||
|
justify-content: center; |
||||||
|
align-items: center; |
||||||
|
flex-direction: column; |
||||||
|
gap: 1rem; |
||||||
|
background-color: green; |
||||||
|
padding: 2rem; |
||||||
|
border-radius: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
h1{ |
||||||
|
display: flex; |
||||||
|
justify-content: center; |
||||||
|
align-items: center; |
||||||
|
flex-direction: column; |
||||||
|
gap: 1rem; |
||||||
|
font-size: 3rem; |
||||||
|
background-color: rgb(255, 255, 255); |
||||||
|
text-shadow: 1px 1px #FFFFFF; |
||||||
|
} |
||||||
|
|
||||||
|
button{ |
||||||
|
display: flex; |
||||||
|
justify-content: center; |
||||||
|
align-items: center; |
||||||
|
flex-direction: column; |
||||||
|
gap: 1rem; |
||||||
|
font-size: 3rem; |
||||||
|
background-color: rgb(255, 255, 255); |
||||||
|
text-shadow: 1px 1px #FFFFFF; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
.navbar-custom { |
||||||
|
background-color: #4054bd; |
||||||
|
color: #000; |
||||||
|
position: fixed; |
||||||
|
top: 0; |
||||||
|
width: 100%; |
||||||
|
z-index: 10; |
||||||
|
border-bottom: 1px solid #4f3817; |
||||||
|
border-radius: 0; |
||||||
|
} |
||||||
|
|
||||||
|
.navbar-custom .navbar-brand { |
||||||
|
font-weight: bold; |
||||||
|
font-size: 24px; |
||||||
|
} |
||||||
|
|
||||||
|
.navbar-custom .nav-link { |
||||||
|
color: #f5f5f5; |
||||||
|
font-weight: bold; |
||||||
|
font-size: 18px; |
||||||
|
margin-right: 15px; |
||||||
|
} |
||||||
|
|
||||||
|
.navbar-custom .nav-link:hover { |
||||||
|
color: #888888; |
||||||
|
} |
After Width: | Height: | Size: 8.2 KiB |
After Width: | Height: | Size: 515 KiB |
Before Width: | Height: | Size: 2.6 KiB |
Loading…
Reference in new issue