Se le agregan que el router pueda tener protected routes

develop
Al Garcia 2 years ago
parent 6252e66372
commit 01d8fba0fd
  1. 5
      package.json
  2. 53
      src/App.css
  3. 9
      src/App.test.tsx
  4. 85
      src/App.tsx
  5. 13
      src/Components/Home/Home.tsx
  6. 12
      src/Components/Home/Protected/Protected.tsx
  7. 97
      src/Components/Login/Login.tsx
  8. 32
      src/Components/ProtectedRoute/ProtectedRoute.tsx
  9. 7
      src/Components/Reportes/RptViajesPendientes.tsx
  10. 5
      src/Constants/TargetURL.ts
  11. 4
      src/Interfaces/Auth/IAuth.ts
  12. 7
      src/Interfaces/Auth/IMenu.ts
  13. 6
      src/Interfaces/Auth/IPermisos.ts
  14. 15
      src/Services/Auth/Auth.Services.ts
  15. 52
      src/Services/Auth/config/http-common.ts
  16. 28
      src/index.tsx
  17. 41
      src/store/features/Auth/MenuItemsSlice.ts
  18. 12
      src/store/store.ts
  19. 9102
      yarn.lock

@ -3,6 +3,7 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@reduxjs/toolkit": "^1.8.5",
"@testing-library/jest-dom": "^5.16.5", "@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.3.0", "@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
@ -10,8 +11,12 @@
"@types/node": "^16.11.56", "@types/node": "^16.11.56",
"@types/react": "^18.0.17", "@types/react": "^18.0.17",
"@types/react-dom": "^18.0.6", "@types/react-dom": "^18.0.6",
"axios": "^0.27.2",
"bootstrap": "^5.2.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-redux": "^8.0.2",
"react-router-dom": "^6.3.0",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"typescript": "^4.7.4", "typescript": "^4.7.4",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"

@ -2,37 +2,38 @@
text-align: center; text-align: center;
} }
.App-logo { .Auth-form-container {
height: 40vmin; display: flex;
pointer-events: none; justify-content: center;
align-items: center;
width: 100vw;
height: 100vh;
} }
@media (prefers-reduced-motion: no-preference) { .Auth-form {
.App-logo { width: 420px;
animation: App-logo-spin infinite 20s linear; box-shadow: rgb(0 0 0 / 16%) 1px 1px 10px;
} padding-top: 30px;
padding-bottom: 20px;
border-radius: 8px;
background-color: white;
} }
.App-header { .Auth-form-content {
background-color: #282c34; padding-left: 12%;
min-height: 100vh; padding-right: 12%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
} }
.App-link { .Auth-form-title {
color: #61dafb; text-align: center;
margin-bottom: 1em;
font-size: 24px;
color: rgb(34, 34, 34);
font-weight: 800;
} }
@keyframes App-logo-spin { label {
from { font-size: 14px;
transform: rotate(0deg); font-weight: 600;
} color: rgb(34, 34, 34);
to { }
transform: rotate(360deg);
}
}

@ -1,9 +0,0 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

@ -1,26 +1,71 @@
import React from 'react'; import 'bootstrap/dist/css/bootstrap.min.css'
import logo from './logo.svg'; import { BrowserRouter, Link, Navigate, Outlet, Route, Routes } from 'react-router-dom'
import './App.css'; import './App.css'
import DSAuth from '../src/Services/Auth/Auth.Services'
import { Login } from './Components/Login/Login'
import { RptViajesPendientes } from './Components/Reportes/RptViajesPendientes'
import { Home } from './Components/Home/Home'
import { Protected } from './Components/Home/Protected/Protected'
import { useEffect } from 'react'
import ProtectedRoute, { ProtectedRouteProps } from './Components/ProtectedRoute/ProtectedRoute'
function App() { function App() {
const Userlogued = (): boolean => {
DSAuth.Validate()
.then((response) => {
console.log('Crednciales validas')
return true
})
.catch((e: Error) => {
console.log('Credenciales apocrifas')
return false
})
return false
}
useEffect(() => {
Userlogued()
console.log('Entro al proceso de router')
}, [])
function PageNotFound() {
return (
<div>
<h2>Sorry, no matching page</h2>
</div>
)
}
const ProtectedRoute = ({ auth = false, redirectPath = '/login' }) => {
if (!auth) {
return <Navigate to={redirectPath} replace />
}
return <Outlet />
}
return ( return (
<div className="App"> <div className='App'>
<header className="App-header"> <BrowserRouter>
<img src={logo} className="App-logo" alt="logo" /> <Link to='login'>login</Link>
<p> <br />
Edit <code>src/App.tsx</code> and save to reload. <Link to='RptViajesPendientes'>Reporte de viajes</Link>
</p> <Routes>
<a <Route path='/' element={<Home />}></Route>
className="App-link" <Route path='login' element={<Login />}></Route>
href="https://reactjs.org" <Route element={<ProtectedRoute auth={Userlogued()} />}>
target="_blank" <Route path='RptViajesPendientes' element={<RptViajesPendientes />} />
rel="noopener noreferrer" </Route>
> <Route path='*' element={<PageNotFound />} />
Learn React </Routes>
</a> </BrowserRouter>
</header>
</div> </div>
); )
} }
export default App; export default App
/* export default App
function useSessionContext(): [any, any] {
throw new Error('Function not implemented.')
} */

@ -0,0 +1,13 @@
import React, { FC } from 'react'
import { Outlet } from 'react-router-dom'
interface IProps {}
export const Home: FC<IProps> = (props) => {
return (
<div>
<h1>This is the Guest Layout Page</h1>
<Outlet />
</div>
)
}

@ -0,0 +1,12 @@
import React, { FC } from 'react'
interface IProps {}
/**
* @author
* @function @Protected
**/
export const Protected: FC<IProps> = (props) => {
return <div>Protected</div>
}

@ -0,0 +1,97 @@
import { FC, useEffect, useState } from 'react'
import { Navigate, useNavigate } from 'react-router-dom'
import IAuth from '../../Interfaces/Auth/IAuth'
import DSAuth from '../../Services/Auth/Auth.Services'
interface IProps {}
export const Login: FC<IProps> = (props) => {
const navigate = useNavigate()
const [Usuario, setUsuario] = useState('')
const [Contrasena, setContrasena] = useState('')
const [token, setToken] = useState<string>(
(window.localStorage.getItem('token') ? window.localStorage.getItem('token') : '')!
)
const Login = () => {
const data: IAuth = {
usuario: Usuario,
contrasena: Contrasena,
}
DSAuth.login(data)
.then((response) => {
localStorage.setItem('token', response.data.token)
setToken(response.data.token)
window.location.href = 'http://localhost:3000/login'
})
.catch((e: Error) => {
alert('Credeciales invalidas!')
})
}
const ValidateToken = () => {
if (token.length > 10) {
DSAuth.Validate()
.then((response) => {
navigate('../RptViajesPendientes', { replace: true })
})
.catch((e: Error) => {
// navigate('login', { replace: true })
})
}
}
useEffect(() => {
ValidateToken()
}, [])
useEffect(() => {
ValidateToken()
}, [])
return (
<div>
{token && <Navigate to='RptViajesPendientes' replace={true} />}
<div className='Auth-form-container'>
<form className='Auth-form'>
<div className='Auth-form-content'>
<h3 className='Auth-form-title'>AOL</h3>
<div className='form-group mt-3'>
<label>Usuario</label>
<input
type='email'
className='form-control mt-1'
placeholder='Usuario'
onChange={(e) => {
setUsuario(e.target.value)
}}
/>
</div>
<div className='form-group mt-3'>
<label>Contraseña</label>
<input
type='password'
className='form-control mt-1'
placeholder='Contraseña'
onChange={(e) => {
setContrasena(e.target.value)
}}
/>
</div>
<div className='d-grid gap-2 mt-3'>
<button
type='button'
className='btn btn-primary'
onClick={() => {
Login()
}}
>
login
</button>
</div>
</div>
</form>
</div>
</div>
)
}

@ -0,0 +1,32 @@
import { useEffect } from 'react'
import { Navigate, useLocation } from 'react-router'
export type ProtectedRouteProps = {
isAuthenticated: boolean
authenticationPath: string
redirectPath: string
setRedirectPath: (path: string) => void
outlet: JSX.Element
}
export default function ProtectedRoute({
isAuthenticated,
authenticationPath,
redirectPath,
setRedirectPath,
outlet,
}: ProtectedRouteProps) {
const currentLocation = useLocation()
useEffect(() => {
if (!isAuthenticated) {
setRedirectPath(currentLocation.pathname)
}
}, [isAuthenticated, setRedirectPath, currentLocation])
if (isAuthenticated && redirectPath === currentLocation.pathname) {
return outlet
} else {
return <Navigate to={{ pathname: isAuthenticated ? redirectPath : authenticationPath }} />
}
}

@ -0,0 +1,7 @@
import React, { FC } from 'react'
interface IProps {}
export const RptViajesPendientes: FC<IProps> = (props) => {
return <div>RptViajesPendientes</div>
}

@ -0,0 +1,5 @@
export class TargetURL {
get() {
return (!process.env.NODE_ENV || process.env.NODE_ENV === 'development') ? "https://localhost:7000/api" : "http://reportes.gemcousa.com:5000/api"
}
}

@ -0,0 +1,4 @@
export default interface IAuth {
usuario: string,
contrasena: string
}

@ -0,0 +1,7 @@
export default interface IMenu {
id: number,
descripcion: string,
padreId: number,
posicion: number,
url: string
}

@ -0,0 +1,6 @@
import IMenu from "./IMenu";
export default interface IPermisos {
menu : IMenu[],
token: string
}

@ -0,0 +1,15 @@
import http from "../../Services/Auth/config/http-common";
import IAuth from "../../Interfaces/Auth/IAuth"
//import ItemMenuData from "../../Interfaces/Auth/IMenu";
import IPermisos from "../../Interfaces/Auth/IPermisos";
//import Token from '../../Interfaces/token'
class authDataService {
login(data: IAuth) {
return http.post<IPermisos>("Auth/Login", data);
}
Validate() {
return http.get<String>("Auth/Validate");
}
}
export default new authDataService();

@ -0,0 +1,52 @@
import axios from "axios";
import { TargetURL } from '../../../Constants/TargetURL'
const token = localStorage.getItem("token");
const URL = new TargetURL()
const instance = axios.create({
baseURL: URL.get(),
headers: {
"Content-type": "application/json",
"Authorization": (token) ? `Bearer ${token}` : ''
},
});
instance.interceptors.response.use(function (response) {
//if (process.env.NODE_ENV === 'development') console.log('log: '+JSON.stringify(response))
return response;
}, function (error) {
if ( (window.location.href.indexOf("login")>0) || (window.location.href.indexOf("reset")>0) ) {
return Promise.reject(error);
}
else if (401 === error.response.status) {
localStorage.clear();
window.location.href = "/login";
} else if ((409 === error.response.status) && (error.response.data.respuesta.indexOf('factura'))) {
console.log(JSON.stringify(error.response))
if (error.response.data.registro.factura) {
(error.response.data.registro.id===-1) ?
alert(error.response.data.respuesta +
' en la factura: ' +
error.response.data.registro.factura +
' en el trafico: CG' +
error.response.data.registro.idTrafico) :
alert(error.response.data.respuesta + ' en TRADE')
}
} else {
return Promise.reject(error);
}
});
instance.interceptors.request.use(function (config) {
/* if (process.env.NODE_ENV === 'development')
console.log('log: '+JSON.stringify(config))*/
return config;
}, function (error) {
return Promise.reject(error);
});
export default instance;

@ -1,19 +1,21 @@
import React from 'react'; import React from 'react'
import ReactDOM from 'react-dom/client'; import ReactDOM from 'react-dom/client'
import './index.css'; import './index.css'
import App from './App'; import App from './App'
import reportWebVitals from './reportWebVitals'; import { Provider } from 'react-redux'
import { store } from './store/store'
import reportWebVitals from './reportWebVitals'
const root = ReactDOM.createRoot( const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
document.getElementById('root') as HTMLElement
);
root.render( root.render(
<React.StrictMode> <Provider store={store}>
<App /> <React.StrictMode>
</React.StrictMode> <App />
); </React.StrictMode>
</Provider>
)
// If you want to start measuring performance in your app, pass a function // If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log)) // to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals(); reportWebVitals()

@ -0,0 +1,41 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import IMenu from '../../../Interfaces/Auth/IMenu';
const MenuItems: IMenu[] = [{ id:0, descripcion:'', padreId:0, posicion:0, url:''}]
const initialState = { MenuItems }
export const MenuItemsSlice = createSlice({
name: 'MenuItems',
initialState: initialState,
reducers: {
populateMenuItems : (state, action: PayloadAction<IMenu[]>) => {
state.MenuItems = []
state.MenuItems.push(... action.payload)
},
addMenuItems : (state, action: PayloadAction<IMenu>) => {
var Existe = state.MenuItems.find(function(item) {
return item.id === action.payload.id;
});
if (!Existe) state.MenuItems.push(action.payload)
},
updateMenuItems : (state, action: PayloadAction<IMenu>) => {
const i = state.MenuItems.findIndex(_element => _element.id === action.payload.id);
if (i > -1) state.MenuItems[i] = action.payload;
else state.MenuItems.push(action.payload);
},
deleteMenuItems : (state, action: PayloadAction<number>) => {
const newArr = state.MenuItems.filter(data => data.id != action.payload);
state.MenuItems=newArr
},
InitMenuItems : (state, action: PayloadAction<number>) => {
state.MenuItems.splice(0,state.MenuItems.length-1)
},
},
})
export const { addMenuItems,
populateMenuItems,
updateMenuItems,
deleteMenuItems,
InitMenuItems } = MenuItemsSlice.actions;
export default MenuItemsSlice.reducer;

@ -0,0 +1,12 @@
import { configureStore } from '@reduxjs/toolkit'
import { MenuItemsSlice } from './features/Auth/MenuItemsSlice'
export const store = configureStore({
reducer: {
// MenuItemsSlice: MenuItemsSlice
}
})
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save