diff --git a/package.json b/package.json index ced9dca458bb8278f4cbaa884ce86e1675849348..20b9a5e3295a90964a98092f613016a370776dc5 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,6 @@ "@testing-library/user-event": "^12.1.10", "@tinymce/tinymce-react": "^3.12.6", "axios": "^0.21.1", - "jwt-decode": "^3.1.2", "react": "^17.0.2", "react-dom": "^17.0.2", "react-router-dom": "^5.2.0", diff --git a/src/App.tsx b/src/App.tsx index ea280ca675cf325259775f7ef7112fb0e0f7e984..346a66d2c96875749143bd1d3e98d297e82d7188 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,15 +1,36 @@ -import React, { createContext } from 'react' +import { useState, useEffect } from 'react' import { BrowserRouter } from 'react-router-dom' import Layout from './components/Layout/Layout' import Routes from './components/Routes/Routes' import { useAuth } from './hooks/useAuth' import { UserContext } from './hooks/userContext' +import { User } from './models/user.model' function App() { - const { user, setUser, loginUser, isLogged } = useAuth() + const { loginUser, error, isUserLogged, getUser, logoutUser } = useAuth() + const [isLogged, setisLogged] = useState<boolean>(false) + const [user, setuser] = useState<User>() + + useEffect(() => { + let subscribed = true + async function checkIsLogged() { + const userLogged = await isUserLogged() + if (userLogged) { + setisLogged(true) + setuser(getUser() as User) + } else setisLogged(false) + } + if (subscribed) checkIsLogged() + return () => { + subscribed = false + } + }, [isLogged]) + return ( <BrowserRouter> - <UserContext.Provider value={{ user, setUser, isLogged, loginUser }}> + <UserContext.Provider + value={{ user, loginUser, error, isLogged, logoutUser, setisLogged }} + > <Layout> <Routes /> </Layout> diff --git a/src/components/EcolyoLink/EcolyoLink.tsx b/src/components/EcolyoLink/EcolyoLink.tsx new file mode 100644 index 0000000000000000000000000000000000000000..74cebd68a7713312216ee81bdec73abfd66ef4ed --- /dev/null +++ b/src/components/EcolyoLink/EcolyoLink.tsx @@ -0,0 +1,23 @@ +import React, { ChangeEvent, useState } from 'react' +import './ecolyoLink.scss' +const EcolyoLink: React.FC = () => { + const baseEcolyoUrl = 'https://ecolyo.{cozyUrl}/' + const [link, setlink] = useState<string>('') + const handleChange = (e: ChangeEvent<HTMLInputElement>) => { + setlink(e.target.value) + } + return ( + <div className="ecolyo-link"> + <h2>Sélectionner un lien interne</h2> + <span>{baseEcolyoUrl}</span> + <input + type="text" + className="input-dark" + value={link} + onChange={handleChange} + /> + </div> + ) +} + +export default EcolyoLink diff --git a/src/components/EcolyoLink/ecolyoLink.scss b/src/components/EcolyoLink/ecolyoLink.scss new file mode 100644 index 0000000000000000000000000000000000000000..4bab51e00c9d0522caabdddca24e9f1a2a9f571c --- /dev/null +++ b/src/components/EcolyoLink/ecolyoLink.scss @@ -0,0 +1,12 @@ +@import '../../styles/config/typography'; +@import '../../styles/config/colors'; +.ecolyo-link { + margin: 1.5rem 0; + span { + display: inline-block; + margin-top: 1rem; + @include text-medium; + color: $gold; + font-size: 1rem; + } +} diff --git a/src/components/Editing/Editing.tsx b/src/components/Editing/Editing.tsx index 2111a565f958ee76349695b945f05c46d4c29a3a..de5c3fa6e72a5907b496a4d05030659e4bae1988 100644 --- a/src/components/Editing/Editing.tsx +++ b/src/components/Editing/Editing.tsx @@ -3,24 +3,28 @@ import { Editor } from '@tinymce/tinymce-react' import DateSelector from '../DateSelector/DateSelector' import './editing.scss' import { MonthlyNewsService } from '../../services/monthlyNews.service' -import { UserContext } from '../../hooks/userContext' +import { UserContext, UserContextProps } from '../../hooks/userContext' import { MonthlyNews } from '../../models/monthlyNews.model' +import EcolyoLink from '../EcolyoLink/EcolyoLink' +import Poll from '../Poll/Poll' const Editing: React.FC = () => { const [date, setDate] = useState<Date>(new Date()) const [header, setHeader] = useState<string>('') const [quote, setQuote] = useState<string>('') const [isTouched, setIsTouched] = useState<boolean>(false) - const { user }: any = useContext(UserContext) + const { user }: Partial<UserContextProps> = useContext(UserContext) const handleSave = async (): Promise<void> => { - const monthlyNewsService = new MonthlyNewsService() - await monthlyNewsService.createMonthlyReport( - date, - header, - quote, - user.xsrftoken - ) + if (user) { + const monthlyNewsService = new MonthlyNewsService() + await monthlyNewsService.createMonthlyReport( + date, + header, + quote, + user.xsrftoken + ) + } } const handleCancel = useCallback(() => { setQuote('') @@ -46,21 +50,30 @@ const Editing: React.FC = () => { } useEffect(() => { + let subscribed = true async function getCurrentMonthlyNews() { - const monthlyNewsService = new MonthlyNewsService() - const news: MonthlyNews = await monthlyNewsService.getSingleMonthlyReport( - date.getFullYear(), - date.getMonth(), - user.xsrftoken - ) - if (news) { - setHeader(news.header) - setQuote(news.quote) - setIsTouched(false) + if (user) { + const monthlyNewsService = new MonthlyNewsService() + const news: MonthlyNews = + await monthlyNewsService.getSingleMonthlyReport( + date.getFullYear(), + date.getMonth(), + user.xsrftoken + ) + if (news) { + setHeader(news.header) + setQuote(news.quote) + setIsTouched(false) + } } } - getCurrentMonthlyNews() - }, [date]) + if (subscribed) { + getCurrentMonthlyNews() + } + return () => { + subscribed = false + } + }, [date, user]) return ( <> @@ -110,6 +123,8 @@ const Editing: React.FC = () => { Sauvegarder </button> </div> + <EcolyoLink /> + <Poll /> </div> </> ) diff --git a/src/components/Login/Login.tsx b/src/components/Login/Login.tsx index 57d35c319b39c2a204d258a224e341281c9b2421..275aad44aaa770d003dbb3eeecef56f4e0a5b6e5 100644 --- a/src/components/Login/Login.tsx +++ b/src/components/Login/Login.tsx @@ -1,23 +1,24 @@ -import React, { useContext, useEffect } from 'react' -import { useHistory } from 'react-router-dom' +import React, { useContext } from 'react' +import { useAuth } from '../../hooks/useAuth' import { UserContext } from '../../hooks/userContext' import './login.scss' const Login: React.FC = () => { - const { user, loginUser } = useContext(UserContext) - const history = useHistory() + const { loginUser, setisLogged } = useContext(UserContext) - useEffect(() => { - if (user) { - history.push('/edition') + const handleClickLogin = async () => { + if (loginUser && setisLogged) { + await loginUser() + + setisLogged(true) } - }, [user]) + } return ( <div className="login"> <div className="container"> <h1>Bienvenue sur le Backoffice d'Ecolyo !</h1> - <button className="btnValid" onClick={loginUser}> + <button className="btnValid" onClick={handleClickLogin}> Login </button> </div> diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index 07e3a2b31de2a309a2a43b6e5b197c4939ead190..8f7fdf8d5aa25024ec1c9056c069f0800e6eb23b 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -1,11 +1,12 @@ -import React, { useState } from 'react' +import React, { useContext } from 'react' import routes from '../../constants/routes.json' import logo from '../../assets/icons/ecolyo-logo.svg' import './menu.scss' import { NavLink } from 'react-router-dom' +import { UserContext } from '../../hooks/userContext' const Menu: React.FC = () => { - const [isLogged] = useState<boolean>(false) + const { isLogged, logoutUser } = useContext(UserContext) return ( <nav className={'menu'}> @@ -23,7 +24,7 @@ const Menu: React.FC = () => { </div> <div className="administration"> {isLogged ? ( - <p>Account</p> + <p onClick={logoutUser}>Logout</p> ) : ( <NavLink to="/login" activeClassName="active"> Login diff --git a/src/components/Navbar/Navbar.tsx b/src/components/Navbar/Navbar.tsx index a8e60e237d61f6a4546e1a706ccfea4418fad7d2..f12bfa4e21bb9ad9de9fa2ca935371bebe898f67 100644 --- a/src/components/Navbar/Navbar.tsx +++ b/src/components/Navbar/Navbar.tsx @@ -1,16 +1,15 @@ -import React, { useState } from 'react' +import React, { useContext } from 'react' import { NavLink } from 'react-router-dom' -import routes from '../../constants/routes.json' -import burger from '../../assets/icons/menu.svg' import logo from '../../assets/icons/ecolyo-logo.svg' import login from '../../assets/icons/login.svg' import editing from '../../assets/icons/editing.svg' import settings from '../../assets/icons/settings.svg' import './navbar.scss' +import { UserContext } from '../../hooks/userContext' const Navbar: React.FC = () => { - const [isLogged] = useState<boolean>(false) + const { isLogged, logoutUser } = useContext(UserContext) return ( <div className="navbar"> <img src={logo} alt="Ecolyo logo" className="logo" /> @@ -24,10 +23,14 @@ const Navbar: React.FC = () => { <img src={settings} className="navbar-icon" alt="Settings icon" /> Paramètres </NavLink> - <NavLink to="/login" activeClassName="active"> - <img src={login} className="navbar-icon" alt="Login icon" /> - Login - </NavLink> + {isLogged ? ( + <p onClick={logoutUser}>Logout</p> + ) : ( + <NavLink to="/login" activeClassName="active"> + <img src={login} className="navbar-icon" alt="Login icon" /> + Login + </NavLink> + )} </div> </div> ) diff --git a/src/components/Poll/Poll.tsx b/src/components/Poll/Poll.tsx new file mode 100644 index 0000000000000000000000000000000000000000..c52e01130bc544471e3c23283fd9959b611dd049 --- /dev/null +++ b/src/components/Poll/Poll.tsx @@ -0,0 +1,20 @@ +import React, { ChangeEvent, useState } from 'react' +const Poll: React.FC = () => { + const [poll, setPoll] = useState<string>('') + const handleChange = (e: ChangeEvent<HTMLInputElement>) => { + setPoll(e.target.value) + } + return ( + <div className="poll"> + <h2>Sélectionner un formulaire</h2> + <input + type="text" + className="input-dark" + value={poll} + onChange={handleChange} + /> + </div> + ) +} + +export default Poll diff --git a/src/components/Routes/PrivateRoute.tsx b/src/components/Routes/PrivateRoute.tsx index 0d97b0e9be5602c90623f8cc288a9fcf280c5c44..a8147f0608a6494463054bf7d67aaffcbad6e3cd 100644 --- a/src/components/Routes/PrivateRoute.tsx +++ b/src/components/Routes/PrivateRoute.tsx @@ -14,6 +14,7 @@ const PrivateRoute: React.FC<PrivateRouteProps> = ({ exact, }: PrivateRouteProps) => { const { isLogged } = useContext(UserContext) + return isLogged ? ( <Route path={path} exact={exact} component={component} /> ) : ( diff --git a/src/components/Routes/Routes.tsx b/src/components/Routes/Routes.tsx index 35a75918b4dbfd978f16a8efb722f528c2f9008a..0f3c8c8ed2eb8fd1094ec626c870dd4eedde3cba 100644 --- a/src/components/Routes/Routes.tsx +++ b/src/components/Routes/Routes.tsx @@ -1,13 +1,17 @@ -import React from 'react' +import React, { useContext } from 'react' import { Redirect, Route, Switch } from 'react-router-dom' +import { UserContext } from '../../hooks/userContext' import Editing from '../Editing/Editing' import Login from '../Login/Login' import Settings from '../Settings/Settings' import PrivateRoute from './PrivateRoute' const Routes: React.FC = () => { + const { isLogged } = useContext(UserContext) + console.log('routelogged ?', isLogged) return ( <Switch> + {isLogged && <Redirect path="/login" to="/editing" />} <Route path="/login" component={Login} /> <PrivateRoute path="/editing" component={Editing} exact /> <PrivateRoute path="/settings" component={Settings} exact /> diff --git a/src/hooks/useAuth.ts b/src/hooks/useAuth.ts index 38f20c396598955a5b3e138f76c42c454a88fd39..f9d7e53207f9d73c4183b6dfa6f26c9cb420cad6 100644 --- a/src/hooks/useAuth.ts +++ b/src/hooks/useAuth.ts @@ -1,36 +1,59 @@ -import React, { useState, useContext } from 'react' -import { useHistory } from 'react-router-dom' +import React, { useState } from 'react' import axios from 'axios' -import { UserContext } from './userContext' import { User } from '../models/user.model' const _apiUrl: string = 'https://localhost:1443/' export interface Auth { - user: User - setUser: React.Dispatch<any> - loginUser: () => void + loginUser: () => Promise<void> error: null - isLogged: boolean + isUserLogged: () => Promise<boolean> + getUser: () => User | undefined + logoutUser: () => void } export const useAuth = (): Auth => { - const [user, setUser] = useState<any>(null) - const [isLogged, setisLogged] = useState(false) const [error, setError] = useState(null) //login user - const loginUser = async () => { + const loginUser = async (): Promise<void> => { try { await axios.get(`${_apiUrl}OAuth2Login`) const { data } = await axios.get(`${_apiUrl}api/common/WhoAmI`) if (data) { - setisLogged(true) - console.log(data) - setUser(data) + console.log('whomi', data) + localStorage.setItem('user', JSON.stringify(data)) } } catch (e) { setError(e) } } - return { user, setUser, loginUser, error, isLogged } + + const logoutUser = (): void => { + localStorage.removeItem('user') + } + + const isUserLogged = async (): Promise<boolean> => { + const user = localStorage.getItem('user') + if (user) { + try { + const { data } = await axios.get(`${_apiUrl}api/common/WhoAmI`) + if (data) { + console.log(data) + return true + } + return false + } catch (e) { + setError(e) + return false + } + } else { + return false + } + } + const getUser = (): User | undefined => { + const user = localStorage.getItem('user') + if (user) return JSON.parse(user) as User + } + + return { loginUser, error, isUserLogged, getUser, logoutUser } } diff --git a/src/hooks/userContext.ts b/src/hooks/userContext.ts index 851a020192f33b125dc97afa982b68aa31930988..895920b54751df8307d763c3ca26492fc0181f98 100644 --- a/src/hooks/userContext.ts +++ b/src/hooks/userContext.ts @@ -1,11 +1,12 @@ -import { createContext } from 'react' +import React, { createContext, Dispatch, SetStateAction } from 'react' import { User } from '../models/user.model' export interface UserContextProps { - user: User - setUser: React.Dispatch<any> - loginUser: () => void + loginUser: () => Promise<void> error: null isLogged: boolean + user: User + logoutUser: () => void + setisLogged: Dispatch<SetStateAction<boolean>> } export const UserContext = createContext<Partial<UserContextProps>>({}) diff --git a/yarn.lock b/yarn.lock index d88552004718232d3ddf0a3c3d2d1f102076e4c7..f58501a2402a43273416a5e090c09f60139d5c84 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7222,11 +7222,6 @@ jsprim@^1.2.2: array-includes "^3.1.2" object.assign "^4.1.2" -jwt-decode@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59" - integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A== - killable@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892"