diff options
| author | Paweł Bernaciak <pawelbernaciak@zohomail.eu> | 2023-10-27 16:09:31 +0200 |
|---|---|---|
| committer | Paweł Bernaciak <pawelbernaciak@zohomail.eu> | 2023-10-27 16:09:31 +0200 |
| commit | 33d1d72d5e7f2e8e7f846bbf8651d7f128765c64 (patch) | |
| tree | 1b45b9cf6380a7910fa2d37c723af6a1432d5de3 /frontend/src | |
| parent | 842aaba2300b295f6e046bfaf9f34cb556e203b8 (diff) | |
New frontend project
Diffstat (limited to 'frontend/src')
| -rw-r--r-- | frontend/src/App.tsx | 130 | ||||
| -rw-r--r-- | frontend/src/components/ElementList.tsx | 46 | ||||
| -rw-r--r-- | frontend/src/components/ElementView.tsx | 19 | ||||
| -rw-r--r-- | frontend/src/index.css | 3 | ||||
| -rw-r--r-- | frontend/src/index.tsx | 21 | ||||
| -rw-r--r-- | frontend/src/main.tsx | 13 | ||||
| -rw-r--r-- | frontend/src/reportWebVitals.tsx | 13 | ||||
| -rw-r--r-- | frontend/src/setupTests.tsx | 5 | ||||
| -rw-r--r-- | frontend/src/types.ts | 26 | ||||
| -rw-r--r-- | frontend/src/vite-env.d.ts | 1 |
10 files changed, 191 insertions, 86 deletions
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 996a794..7aae66c 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,58 +1,94 @@ -import React from 'react'; import { CredentialResponse, GoogleLogin } from '@react-oauth/google'; +import { FC, useEffect, useState } from 'react'; +import { LoginRequest, LoginResponse, User } from './types'; +import ElementList from './components/ElementList'; -interface LoginResponse { - id: number -} +const App: FC = () => { + const [user, setUser] = useState<User | undefined>(undefined); -interface User { - id: Number, - elements: Element[], -} + useEffect(() => { + const userString = localStorage.getItem('user'); + if (userString == null) { + return; + } -interface Element { - id: Number, - userId: Number, - name: string, - state: ElementState, -} + setUser(JSON.parse(userString)); + }, []); -enum ElementState { - HasColor, - HasIcon, -} + const login = async (credentials: CredentialResponse) => { + if (credentials.credential == null) { + console.log('Error getting credentials from google'); + return; + } -function App() { - const responseMessage = async (googleResponse: CredentialResponse) => { - console.log(googleResponse); - const url: string = "/auth/login"; - const body = { - "googleToken": googleResponse.credential - }; - - const response = await fetch(url, { - method: "POST", - credentials: "include", - mode: "cors", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify(body), - }); - if (!response.ok) { - console.log("Backend conection problem"); - return; - } - - const user = await response.json() as LoginResponse; - console.log(user); + // Authenticate with API and get user ID + const loginRequest: LoginRequest = { + googleToken: credentials.credential, }; + const loginResponse = await fetch('/api/auth/login', { + method: 'POST', + credentials: 'include', + mode: 'cors', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(loginRequest), + }); + if (!loginResponse.ok) { + console.log('Error connecting to API. Code: ', loginResponse.status); + return; + } + const userLoginResponse: LoginResponse = await loginResponse.json(); + + //Get info about user using ID + const userInfoResponse = await fetch(`/api/user/${userLoginResponse.id}`, { + credentials: 'include', + }); + if (!userInfoResponse.ok) { + console.log('Error connecting to API. Code: ', userInfoResponse.status); + return; + } + const userResponse: User = await userInfoResponse.json(); + + localStorage.setItem('user', JSON.stringify(userResponse)); + setUser(userResponse); + }; + + const logout = async (e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => { + e.preventDefault(); + + const response = await fetch('api/auth/logout', { + method: 'POST', + credentials: 'include', + }); + if (response.status != 200) { + console.log('Error logging out. Code: ', response.status); + return; + } + + localStorage.removeItem('user'); + setUser(undefined); + }; - return ( - <div className="App"> - <GoogleLogin onSuccess={responseMessage} /> - </div> - ); + return ( + <div className="flex flex-col"> + <div className="flex flex-row space-x-2 px-3 my-2 justify-end"> + {user ? ( + <> + <p>Hello {user.name}</p> + <a + className="cursor-pointer text-blue-500 underline" + onClick={logout}> + Logout + </a> + </> + ) : ( + <GoogleLogin onSuccess={login} /> + )} + </div> + <ElementList /> + </div> + ); } export default App; diff --git a/frontend/src/components/ElementList.tsx b/frontend/src/components/ElementList.tsx new file mode 100644 index 0000000..20b27dd --- /dev/null +++ b/frontend/src/components/ElementList.tsx @@ -0,0 +1,46 @@ +import { FC, useEffect, useState } from 'react'; +import {Element} from '../types'; +import ElementView from './ElementView'; + +const ElementList: FC = () => { + const [elements, setElements] = useState<Element[]>([]); + + useEffect(() => { + const initialElements: number[] = [1, 2, 3, 4]; + + const elementStateString = localStorage.getItem('elementState'); + if (elementStateString != null) { + setElements(JSON.parse(elementStateString)); + return; + } + + const fetchElements = async () => { + const elems: Element[] = []; + + for (const elemId of initialElements) { + const response = await fetch(`/api/element/${elemId}`); + const elem: Element = await response.json(); + elems.push(elem); + } + + localStorage.setItem('elementState', JSON.stringify(elems)); + setElements(elems); + }; + + fetchElements().catch(console.error); + }, []); + + useEffect(() => { + if (elements.length != 0) { + localStorage.setItem('elementState', JSON.stringify(elements)); + } + }, [elements]) + + return ( + <> + {elements.map(elem => <ElementView key={elem.id} element={elem} />)} + </> + ); +}; + +export default ElementList; diff --git a/frontend/src/components/ElementView.tsx b/frontend/src/components/ElementView.tsx new file mode 100644 index 0000000..4f9d489 --- /dev/null +++ b/frontend/src/components/ElementView.tsx @@ -0,0 +1,19 @@ +import { FC } from 'react'; +import { Element } from '../types'; + +interface ElementViewProps { + element: Element; +} + +const ElementView: FC<ElementViewProps> = ({ element }) => { + return ( + <div className='flex flex-row m-2 rounded-md border border-gray-300 bg-gray-100 w-fit h-fit'> + <div className='flex flex-col items-center'> + <img src={`data:image/png;base64,${element.icon}`} width='80px' height='80px'/> + <p className='my-1 mx-2 text-sm'>{element.name}</p> + </div> + </div> + ); +}; + +export default ElementView; diff --git a/frontend/src/index.css b/frontend/src/index.css new file mode 100644 index 0000000..bd6213e --- /dev/null +++ b/frontend/src/index.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities;
\ No newline at end of file diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx deleted file mode 100644 index 162d1a1..0000000 --- a/frontend/src/index.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import App from './App'; -import reportWebVitals from './reportWebVitals'; -import { GoogleOAuthProvider } from '@react-oauth/google'; - -const rootElem: HTMLElement = document.getElementById('root')!; - -const root = ReactDOM.createRoot(rootElem); -root.render( - <GoogleOAuthProvider clientId='84022739350-83723nr55j7t1ptmhrkn1u7aq7nfo3he.apps.googleusercontent.com'> - <React.StrictMode> - <App /> - </React.StrictMode> - </GoogleOAuthProvider> -); - -// If you want to start measuring performance in your app, pass a function -// to log results (for example: reportWebVitals(console.log)) -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(null); diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx new file mode 100644 index 0000000..4e3e75d --- /dev/null +++ b/frontend/src/main.tsx @@ -0,0 +1,13 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App.tsx' +import './index.css' +import {GoogleOAuthProvider} from '@react-oauth/google'; + +ReactDOM.createRoot(document.getElementById('root')!).render( + <GoogleOAuthProvider clientId={import.meta.env.VITE_GOOGLE_CLIENT_ID}> + <React.StrictMode> + <App /> + </React.StrictMode> + </GoogleOAuthProvider>, +) diff --git a/frontend/src/reportWebVitals.tsx b/frontend/src/reportWebVitals.tsx deleted file mode 100644 index f00fe6c..0000000 --- a/frontend/src/reportWebVitals.tsx +++ /dev/null @@ -1,13 +0,0 @@ -const reportWebVitals = (onPerfEntry: any) => { - if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { - getCLS(onPerfEntry); - getFID(onPerfEntry); - getFCP(onPerfEntry); - getLCP(onPerfEntry); - getTTFB(onPerfEntry); - }); - } -}; - -export default reportWebVitals; diff --git a/frontend/src/setupTests.tsx b/frontend/src/setupTests.tsx deleted file mode 100644 index 8f2609b..0000000 --- a/frontend/src/setupTests.tsx +++ /dev/null @@ -1,5 +0,0 @@ -// jest-dom adds custom jest matchers for asserting on DOM nodes. -// allows you to do things like: -// expect(element).toHaveTextContent(/react/i) -// learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom'; diff --git a/frontend/src/types.ts b/frontend/src/types.ts new file mode 100644 index 0000000..ecdd808 --- /dev/null +++ b/frontend/src/types.ts @@ -0,0 +1,26 @@ +export interface LoginRequest { + googleToken: string; +} + +export interface LoginResponse { + id: number; +} + +export interface User { + id: number; + name: string; + elements: Element[]; +} + +export interface Element { + id: number; + userId: number; + name: string; + state: ElementState; + icon: string; +} + +export enum ElementState { + HasColor, + HasIcon, +} diff --git a/frontend/src/vite-env.d.ts b/frontend/src/vite-env.d.ts new file mode 100644 index 0000000..151aa68 --- /dev/null +++ b/frontend/src/vite-env.d.ts @@ -0,0 +1 @@ +/// <reference types="vite/client" />
\ No newline at end of file |
