diff --git a/frontend/search/components/Login.tsx b/frontend/search/components/Login.tsx new file mode 100644 index 0000000..e05b43d --- /dev/null +++ b/frontend/search/components/Login.tsx @@ -0,0 +1,77 @@ +import { useEffect } from 'react' +import { useLoginQuery } from '../codegen/generates' +import create from 'zustand' +import { useTranslation } from 'react-i18next'; + +function jwtDecode(jwt: string) { + /* Just parses the payload — Be aware that signature is not checked */ + return jwt && JSON.parse(atob(jwt.split('.')[1])) +} + +export interface AuthState { + mail: string, + password: string, + setLogin: (mail: string, password: string) => void + jwt: string, + setJwt: (jwt: string) => void, + logout: () => void +} + +export const useAuthStore = create(set => ({ + mail: '', + password: '', + setLogin: (mail, password) => set( _orig => ({mail, password})), + jwt: '', + setJwt: jwt => set( _orig => ({jwt}) ), + logout: () => { localStorage.removeItem('jwt') + set({mail: '', password: '', jwt: ''}) } +})) + +export function jwtFromLocalStorage() { + return localStorage.getItem('jwt') || '' +} + +export function Login() { + const {t} = useTranslation() + const auth = useAuthStore() + + const { data } = useLoginQuery({auth}, {enabled: Boolean(!auth.jwt && auth.mail && auth.password)}) + + useEffect(() => { + if(auth.jwt) { + localStorage.setItem('jwt', auth.jwt) + } + else { + auth.setJwt(data?.login.jwt || jwtFromLocalStorage()) + } + + if(jwtDecode(auth.jwt).exp < Date.now()/1000) { + console.log('session expired') + auth.logout() + } + }, [auth.jwt, data]) + + if(!auth.jwt) { + return ( +
{event.preventDefault() + auth.setLogin((document.getElementById('mail') as HTMLInputElement).value, + (document.getElementById('password') as HTMLInputElement).value) }}> +   +   + +
+ ) + } else { + return ( +
{event.preventDefault() + auth.logout()}} + style={{width: "100%", textAlign: "right"}}> + +
+ ) + } +} diff --git a/frontend/search/components/ngo/HostOfferLookupWrapper.tsx b/frontend/search/components/ngo/HostOfferLookupWrapper.tsx index 726b0d1..9286c1c 100644 --- a/frontend/search/components/ngo/HostOfferLookupWrapper.tsx +++ b/frontend/search/components/ngo/HostOfferLookupWrapper.tsx @@ -1,19 +1,22 @@ import React from 'react' -import { Auth, useGetOffersQuery } from "../../codegen/generates" -import testAuth from '../util/testAuth.json' +import { useGetOffersQuery } from "../../codegen/generates" import HostOfferLookupTable from "./HostOfferLookupTable" import { Box } from "@mui/material" import { useTranslation } from 'react-i18next' +import { Login, useAuthStore } from '../Login' type HostLookupWrapperProps = Record const HostOfferLookupWrapper = ({}: HostLookupWrapperProps) => { const { t } = useTranslation() + const auth = useAuthStore() const staleTimeMinutes = 60 // hotfix till table settings by user (columns width, filters, sort options, …) are persisted - const {data, isFetching, error} = useGetOffersQuery({auth: testAuth as Auth}, {staleTime: staleTimeMinutes * 60 * 1000}) + const {data, isFetching, error} = useGetOffersQuery({auth}, {staleTime: staleTimeMinutes * 60 * 1000}) return <> + { !data?.get_offers && /** TODO: Show logout, by placing in Header at Layout **/ } + { isFetching &&

{ t('loading…') }

} { error &&

{ t('An error occurred while trying to get data from the backend.') }

} { data && !data.get_offers &&

{ t('Seems like you have no permissions. Please try to login again.') }

} diff --git a/frontend/search/components/util/testAuth.json b/frontend/search/components/util/testAuth.json deleted file mode 100644 index dc2d5a1..0000000 --- a/frontend/search/components/util/testAuth.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "mail": "crewing@example-ngo.com", - "password": "Vr(+cFtUG=rsj2:/]*uR" -} diff --git a/frontend/search/package.json b/frontend/search/package.json index 96e8706..6e4c29b 100644 --- a/frontend/search/package.json +++ b/frontend/search/package.json @@ -32,7 +32,8 @@ "react-collapse-pane": "^2.0.1", "react-dom": "17.0.2", "react-i18next": "^11.15.5", - "react-query": "^3.34.16" + "react-query": "^3.34.16", + "zustand": "^3.7.1" }, "devDependencies": { "@graphql-codegen/cli": "^2.3.0", diff --git a/frontend/search/yarn.lock b/frontend/search/yarn.lock index 7d04546..eb35c96 100644 --- a/frontend/search/yarn.lock +++ b/frontend/search/yarn.lock @@ -5617,3 +5617,8 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zustand@^3.7.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/zustand/-/zustand-3.7.1.tgz#7388f0a7175a6c2fd9a2880b383a4bf6cdf6b7c6" + integrity sha512-wHBCZlKj+bg03/hP+Tzv24YhnqqP8MCeN9ECPDXoF01062SIbnfl3j9O0znkDw1lNTY0a8WN3F///a0UhhaEqg==