summaryrefslogtreecommitdiff
path: root/frontend/src
diff options
context:
space:
mode:
authorPaweł Bernaciak <pawelbernaciak@zohomail.eu>2023-10-27 16:09:31 +0200
committerPaweł Bernaciak <pawelbernaciak@zohomail.eu>2023-10-27 16:09:31 +0200
commit33d1d72d5e7f2e8e7f846bbf8651d7f128765c64 (patch)
tree1b45b9cf6380a7910fa2d37c723af6a1432d5de3 /frontend/src
parent842aaba2300b295f6e046bfaf9f34cb556e203b8 (diff)
New frontend project
Diffstat (limited to 'frontend/src')
-rw-r--r--frontend/src/App.tsx130
-rw-r--r--frontend/src/components/ElementList.tsx46
-rw-r--r--frontend/src/components/ElementView.tsx19
-rw-r--r--frontend/src/index.css3
-rw-r--r--frontend/src/index.tsx21
-rw-r--r--frontend/src/main.tsx13
-rw-r--r--frontend/src/reportWebVitals.tsx13
-rw-r--r--frontend/src/setupTests.tsx5
-rw-r--r--frontend/src/types.ts26
-rw-r--r--frontend/src/vite-env.d.ts1
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