diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..01e4a4c25ee9c7eaeb85eb3dc0f6b8c1946b4736 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,61 @@ +image: docker:git + +services: + - docker:dind + +variables: + DOCKER_DRIVER: overlay2 + DOCKER_TLS_CERTDIR: '' + +stages: + - build + - quality + +build-test: + stage: build + image: node:14.15.4-alpine + before_script: + - apk add git + - apk add bash + script: + - yarn + - yarn build + only: + - dev + - merge_requests + +build: + image: docker:18.09 + services: + - docker:18.09-dind + stage: build + only: + - master + - dev + script: + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + - docker build --pull -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG" --build-arg conf=prod . + - docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG" + +sonarqube: + stage: quality + only: + - dev + - merge_requests + image: registry.forge.grandlyon.com/apoyen2/sonnar-scanner-gl:master + before_script: + - export NODE_PATH=$NODE_PATH:`npm root -g` + - npm install -g typescript + script: + - > + sonar-scanner + -Dsonar.projectName="ecolyo-backoffice-front" + -Dsonar.projectVersion=1.0 + -Dsonar.sourceEncoding=UTF-8 + -Dsonar.projectBaseDir=. + -Dsonar.host.url=${SONAR_URL} + -Dsonar.projectKey=${CI_PROJECT_PATH_SLUG} + -Dsonar.login=${SONAR_TOKEN} + -Dsonar.cpd.exclusions=tests/**,src/**/*.spec.ts* + -Dsonar.qualitygate.wait=true + diff --git a/src/App.tsx b/src/App.tsx index 9829f71b2b8962b19f020aec1a8062f3288969d3..2495ec0a34b50573029838c2607328603cec7508 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,37 +1,17 @@ -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' import { ToastContainer } from 'react-toastify' import 'react-toastify/dist/ReactToastify.css' -function App() { - const { loginUser, error, isUserLogged, getUser, logoutUser } = useAuth() - const [isLogged, setisLogged] = useState<boolean>(false) - const [user, setuser] = useState<User>() +import useFindUser from './hooks/useFindUser' - 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, setisLogged]) +function App() { + const { user, setUser, isLoading } = useFindUser() return ( <BrowserRouter> - <UserContext.Provider - value={{ user, loginUser, error, isLogged, logoutUser, setisLogged }} - > + <UserContext.Provider value={{ user, setUser, isLoading }}> <Layout> <Routes /> </Layout> diff --git a/src/components/Editing/Editing.tsx b/src/components/Editing/Editing.tsx index 5af1ff69c657baad85671132a9e0f0ff45c770b2..43634f7fa6238fab3b0fb9e5d02089c821373a29 100644 --- a/src/components/Editing/Editing.tsx +++ b/src/components/Editing/Editing.tsx @@ -142,7 +142,6 @@ const Editing: React.FC = () => { setIsTouched(false) } } - setisLoading(false) } if (subscribed) { @@ -152,7 +151,7 @@ const Editing: React.FC = () => { subscribed = false setRefreshData(false) } - }, [date, user, refreshData]) + }, [date, user, refreshData, resetFields]) return ( <> diff --git a/src/components/Login/Login.tsx b/src/components/Login/Login.tsx index 275aad44aaa770d003dbb3eeecef56f4e0a5b6e5..9bb9f893bc8c04f465826acbb32134e227590351 100644 --- a/src/components/Login/Login.tsx +++ b/src/components/Login/Login.tsx @@ -1,24 +1,15 @@ -import React, { useContext } from 'react' +import React from 'react' import { useAuth } from '../../hooks/useAuth' -import { UserContext } from '../../hooks/userContext' import './login.scss' const Login: React.FC = () => { - const { loginUser, setisLogged } = useContext(UserContext) - - const handleClickLogin = async () => { - if (loginUser && setisLogged) { - await loginUser() - - setisLogged(true) - } - } + const { loginUser } = useAuth() return ( <div className="login"> <div className="container"> <h1>Bienvenue sur le Backoffice d'Ecolyo !</h1> - <button className="btnValid" onClick={handleClickLogin}> + <button className="btnValid" onClick={loginUser}> Login </button> </div> diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index 26383e996ee0a9f44ed3e2c49306f40fe52f3b31..7c8c1c99b6caddc861b7c741b3e93f4fba96b54f 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -4,16 +4,11 @@ import logo from '../../assets/icons/ecolyo-logo.svg' import './menu.scss' import { NavLink } from 'react-router-dom' import { UserContext } from '../../hooks/userContext' +import { useAuth } from '../../hooks/useAuth' const Menu: React.FC = () => { - const { isLogged, logoutUser, setisLogged } = useContext(UserContext) - - const handleLogout = () => { - if (logoutUser && setisLogged) { - logoutUser() - setisLogged(false) - } - } + const { user } = useContext(UserContext) + const { logoutUser } = useAuth() return ( <nav className={'menu'}> <div className="logo-container"> @@ -29,8 +24,8 @@ const Menu: React.FC = () => { })} </div> <div className="administration"> - {isLogged ? ( - <button className="btnValid logButton" onClick={handleLogout}> + {user ? ( + <button className="btnValid logButton" onClick={logoutUser}> Logout </button> ) : ( diff --git a/src/components/MonthlyNews/MonthlyNews.tsx b/src/components/MonthlyNews/MonthlyNews.tsx index a5befe0fc9cde841914ef405895bc3055ef34810..b3481458a6b30f488c0f11c093ab2fc4af15d2c6 100644 --- a/src/components/MonthlyNews/MonthlyNews.tsx +++ b/src/components/MonthlyNews/MonthlyNews.tsx @@ -34,7 +34,9 @@ const MonthlyNews: React.FC<MonthlyNewsProps> = ({ 'undo redo | bold italic underline | alignleft aligncenter alignright | code | ecolyoLink', }} value={header} - onEditorChange={(newHeader) => handleChange(newHeader, 'header')} + onEditorChange={(newHeader: string) => + handleChange(newHeader, 'header') + } /> <div className="subtitle"> <p className="title">Citation du mois</p> @@ -102,7 +104,7 @@ const MonthlyNews: React.FC<MonthlyNewsProps> = ({ }, }} value={quote} - onEditorChange={(newQuote) => handleChange(newQuote, 'quote')} + onEditorChange={(newQuote: string) => handleChange(newQuote, 'quote')} /> <div className="buttons"> <button className="btnCancel" onClick={onCancel}> diff --git a/src/components/Navbar/Navbar.tsx b/src/components/Navbar/Navbar.tsx index f12bfa4e21bb9ad9de9fa2ca935371bebe898f67..a41458c379d7249eb4181c7c12c5d1e080de9e62 100644 --- a/src/components/Navbar/Navbar.tsx +++ b/src/components/Navbar/Navbar.tsx @@ -1,19 +1,16 @@ import React, { useContext } from 'react' import { NavLink } from 'react-router-dom' -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' +import { useAuth } from '../../hooks/useAuth' const Navbar: React.FC = () => { - const { isLogged, logoutUser } = useContext(UserContext) + const { user } = useContext(UserContext) + const { logoutUser } = useAuth() return ( <div className="navbar"> - <img src={logo} alt="Ecolyo logo" className="logo" /> - <div className="menu-list"> <NavLink to={'/editing'} activeClassName="active"> <img src={editing} className="navbar-icon" alt="Editing icon" /> @@ -23,15 +20,12 @@ const Navbar: React.FC = () => { <img src={settings} className="navbar-icon" alt="Settings icon" /> Paramètres </NavLink> - {isLogged ? ( - <p onClick={logoutUser}>Logout</p> - ) : ( - <NavLink to="/login" activeClassName="active"> - <img src={login} className="navbar-icon" alt="Login icon" /> - Login - </NavLink> - )} </div> + {user && ( + <button className="btnValid logButton" onClick={logoutUser}> + Logout + </button> + )} </div> ) } diff --git a/src/components/Navbar/navbar.scss b/src/components/Navbar/navbar.scss index b39e88051c760d2db58c9944698ee1b1c42d5057..78f8bc8988fac89c94c76cbae9026bed80928374 100644 --- a/src/components/Navbar/navbar.scss +++ b/src/components/Navbar/navbar.scss @@ -16,12 +16,6 @@ align-items: center; justify-content: center; } - .burger, - .logo { - width: 2rem; - left: 1rem; - position: absolute; - } a { text-decoration: none; display: flex; @@ -41,4 +35,10 @@ .navbar-icon { width: 1.3rem; } + .logButton { + width: 100px; + min-width: 0; + margin-left: auto; + margin-top: 0; + } } diff --git a/src/components/Poll/Poll.tsx b/src/components/Poll/Poll.tsx index 4ed6d85b09d050b36b0ce639869c355430a85baf..25c71fb08287e6decc8071a9171656bdad06ec2e 100644 --- a/src/components/Poll/Poll.tsx +++ b/src/components/Poll/Poll.tsx @@ -1,70 +1,69 @@ -import { Editor } from '@tinymce/tinymce-react' -import React, { ChangeEvent } from 'react' -import { ContentItems } from '../Editing/Editing' -import './poll.scss' - -interface PollProps { - question: string - link: string - handleChange: ( - value: string, - type: 'header' | 'quote' | 'question' | 'link' - ) => void - onSave: () => Promise<void> - onCancel: () => void - onDelete: (target: ContentItems) => void -} -const Poll: React.FC<PollProps> = ({ - question, - link, - handleChange, - onSave, - onCancel, - onDelete, -}: PollProps) => { - const handleChangeLink = (e: ChangeEvent<HTMLInputElement>) => { - handleChange(e.target.value, 'link') - } - - return ( - <div className="poll"> - <h2>Ajouter un sondage</h2> - - <p className="title">Lien</p> - <input - type="text" - className="input-dark" - value={link} - onChange={handleChangeLink} - /> - <div> - <p className="title">Question</p> - - <Editor - init={{ - menubar: false, - toolbar: - 'undo redo | bold italic underline | alignleft aligncenter alignright | code | ecolyoLink', - }} - value={question} - onEditorChange={(newQuestion, editor) => - handleChange(newQuestion, 'question') - } - /> - <div className="buttons"> - <button className="btnCancel" onClick={onCancel}> - Annuler - </button> - <button className="btnValid" onClick={onSave}> - Sauvegarder - </button> - <button className="btnDelete" onClick={() => onDelete('poll')}> - Supprimer - </button> - </div> - </div> - </div> - ) -} - -export default Poll +import { Editor } from '@tinymce/tinymce-react' +import React, { ChangeEvent } from 'react' +import { ContentItems } from '../Editing/Editing' +import './poll.scss' + +interface PollProps { + question: string + link: string + handleChange: ( + value: string, + type: 'header' | 'quote' | 'question' | 'link' + ) => void + onSave: () => Promise<void> + onCancel: () => void + onDelete: (target: ContentItems) => void +} +const Poll: React.FC<PollProps> = ({ + question, + link, + handleChange, + onSave, + onCancel, + onDelete, +}: PollProps) => { + const handleChangeLink = (e: ChangeEvent<HTMLInputElement>) => { + handleChange(e.target.value, 'link') + } + + return ( + <div className="poll"> + <h2>Ajouter un sondage</h2> + <p className="title">Lien</p> + <input + type="text" + className="input-dark" + value={link} + onChange={handleChangeLink} + /> + <div> + <p className="title">Question</p> + + <Editor + init={{ + menubar: false, + toolbar: + 'undo redo | bold italic underline | alignleft aligncenter alignright | code | ecolyoLink', + }} + value={question} + onEditorChange={(newQuestion: string) => + handleChange(newQuestion, 'question') + } + /> + <div className="buttons"> + <button className="btnCancel" onClick={onCancel}> + Annuler + </button> + <button className="btnValid" onClick={onSave}> + Sauvegarder + </button> + <button className="btnDelete" onClick={() => onDelete('poll')}> + Supprimer + </button> + </div> + </div> + </div> + ) +} + +export default Poll diff --git a/src/components/Routes/PrivateRoute.tsx b/src/components/Routes/PrivateRoute.tsx index a8147f0608a6494463054bf7d67aaffcbad6e3cd..0f2fc3e428fdbef04645a0e0bf673c81528ff8ac 100644 --- a/src/components/Routes/PrivateRoute.tsx +++ b/src/components/Routes/PrivateRoute.tsx @@ -13,9 +13,9 @@ const PrivateRoute: React.FC<PrivateRouteProps> = ({ path, exact, }: PrivateRouteProps) => { - const { isLogged } = useContext(UserContext) + const { user } = useContext(UserContext) - return isLogged ? ( + return user ? ( <Route path={path} exact={exact} component={component} /> ) : ( <Redirect to="/login" /> diff --git a/src/components/Routes/Routes.tsx b/src/components/Routes/Routes.tsx index adb12ee3a80cf0c98547d2dabe944c62faaa8b72..a5c1b43a429265966d775277bcaf39f98fafcbd4 100644 --- a/src/components/Routes/Routes.tsx +++ b/src/components/Routes/Routes.tsx @@ -7,11 +7,11 @@ import Settings from '../Settings/Settings' import PrivateRoute from './PrivateRoute' const Routes: React.FC = () => { - const { isLogged } = useContext(UserContext) + const { user } = useContext(UserContext) return ( <Switch> - {isLogged && <Redirect path="/login" to="/editing" />} + {user && <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 f9d7e53207f9d73c4183b6dfa6f26c9cb420cad6..aa4e1c1b1264799e29a4de3ad8bdff3a5dd99556 100644 --- a/src/hooks/useAuth.ts +++ b/src/hooks/useAuth.ts @@ -1,59 +1,52 @@ -import React, { useState } from 'react' +import { useContext, useState } from 'react' import axios from 'axios' -import { User } from '../models/user.model' +import { UserContext } from './userContext' +import { useHistory } from 'react-router-dom' const _apiUrl: string = 'https://localhost:1443/' export interface Auth { loginUser: () => Promise<void> error: null - isUserLogged: () => Promise<boolean> - getUser: () => User | undefined logoutUser: () => void } export const useAuth = (): Auth => { const [error, setError] = useState(null) + const { setUser } = useContext(UserContext) + const history = useHistory() //login user const loginUser = async (): Promise<void> => { try { await axios.get(`${_apiUrl}OAuth2Login`) - const { data } = await axios.get(`${_apiUrl}api/common/WhoAmI`) - if (data) { - console.log('whomi', data) - localStorage.setItem('user', JSON.stringify(data)) - } + await setUserContext() } catch (e) { setError(e) } } - const logoutUser = (): void => { - localStorage.removeItem('user') + const logoutUser = async (): Promise<void> => { + try { + if (setUser) setUser(null) + await axios.get(`${_apiUrl}Logout`) + } catch (e) { + setError(e) + } } - 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 + //set user in context and push them home + const setUserContext = async (): Promise<void> => { + try { + const { data } = await axios.get(`${_apiUrl}api/common/WhoAmI`) + if (data && setUser) { + setUser(data) + console.log('usertoContext', data) + history.push('/editing') } - } else { - return false + } catch (e) { + setError(e) } } - const getUser = (): User | undefined => { - const user = localStorage.getItem('user') - if (user) return JSON.parse(user) as User - } - return { loginUser, error, isUserLogged, getUser, logoutUser } + return { loginUser, error, logoutUser } } diff --git a/src/hooks/useFindUser.ts b/src/hooks/useFindUser.ts new file mode 100644 index 0000000000000000000000000000000000000000..5a3c38eedb100f8bec0519f5895799fa229440fa --- /dev/null +++ b/src/hooks/useFindUser.ts @@ -0,0 +1,27 @@ +import { useState, useEffect } from 'react' +import axios from 'axios' +import { User } from '../models/user.model' + +const useFindUser = () => { + const [user, setUser] = useState<User | null>(null) + const [isLoading, setLoading] = useState<boolean>(true) + const _apiUrl: string = 'https://localhost:1443/' + + useEffect(() => { + async function findUser() { + const { data } = await axios.get(`${_apiUrl}api/common/WhoAmI`) + if (data) { + setUser(data) + setLoading(false) + } + } + findUser() + }, []) + return { + user, + setUser, + isLoading, + } +} + +export default useFindUser diff --git a/src/hooks/userContext.ts b/src/hooks/userContext.ts index 895920b54751df8307d763c3ca26492fc0181f98..e2f6936764e83d2e058302e7fb4a835aa859ff82 100644 --- a/src/hooks/userContext.ts +++ b/src/hooks/userContext.ts @@ -1,12 +1,9 @@ -import React, { createContext, Dispatch, SetStateAction } from 'react' +import { createContext, Dispatch, SetStateAction } from 'react' import { User } from '../models/user.model' export interface UserContextProps { - loginUser: () => Promise<void> - error: null - isLogged: boolean - user: User - logoutUser: () => void - setisLogged: Dispatch<SetStateAction<boolean>> + user: User | null + setUser: Dispatch<SetStateAction<User | null>> + isLoading: boolean } export const UserContext = createContext<Partial<UserContextProps>>({}) diff --git a/src/services/monthlyNews.service.ts b/src/services/monthlyNews.service.ts index eca0a3347b497532d1a9fe35a2e08b4170b132cb..920018e7fd85a95c928eaa63520effaa817725a8 100644 --- a/src/services/monthlyNews.service.ts +++ b/src/services/monthlyNews.service.ts @@ -58,9 +58,9 @@ export class MonthlyNewsService { }, } ) - if (data == {}) { - return null - } + // if (data == {}) { + // return null + // } return data as IMonthlyNews } catch (e) { @@ -86,9 +86,9 @@ export class MonthlyNewsService { }, } ) - if (data === {}) { - return null - } + // if (data === {}) { + // return null + // } return data as IPoll } catch (e) { console.log('error', e)