Started with web application

This commit is contained in:
cami 2021-08-28 23:42:33 +02:00
parent 7beda138c5
commit 75f2365024
40 changed files with 17382 additions and 0 deletions

25
.gitignore vendored
View File

@ -424,3 +424,28 @@ TSWLatexianTemp*
# Uncomment the next line to have this generated file ignored.
#*Notes.bib
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
frontend/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.vscode/

11
backend/Dockerfile Normal file
View File

@ -0,0 +1,11 @@
FROM python
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY /src .
CMD [ "flask", "run" ]

15
backend/Dockerfile.prod Normal file
View File

@ -0,0 +1,15 @@
FROM python
WORKDIR /app
RUN apt-get -y update && apt-get -y upgrade
COPY requirements.txt .
RUN apt-get -y install sqlite3
RUN pip install -r requirements.txt
COPY /src .
CMD [ "python", "app.py" ]

6
backend/requirements.txt Normal file
View File

@ -0,0 +1,6 @@
click==8.0.1
Flask==2.0.1
itsdangerous==2.0.1
Jinja2==3.0.1
MarkupSafe==2.0.1
Werkzeug==2.0.1

30
backend/src/app.py Normal file
View File

@ -0,0 +1,30 @@
import os
import time
import flask
app = flask.Flask(__name__)
# Read environment variables
if "DEBUG" in os.environ and os.environ["DEBUG"] == 'yes':
debug = True
else:
debug = False
if "HOST" in os.environ:
host = os.environ["HOST"]
else:
host = '0.0.0.0'
if "PORT" in os.environ:
port = int(os.environ["PORT"])
else:
port = 5000
@app.route('/time')
def get_current_time():
return {'time': time.time()}
@app.route('/api/generate-letter', methods=['POST'])
def create_letter():
req = flask.request.get_json(force=True)
print(req)
ret = {'message': 'Erfolgreich'}
return ret, 200

27
docker-compose.yml Normal file
View File

@ -0,0 +1,27 @@
version: '3.7'
services:
frontend-dev:
container_name: frontend-dev
build:
context: ./frontend
dockerfile: Dockerfile
tty: true
volumes:
- "./frontend:/app:z"
ports:
- 3000:3000
environment:
- CHOKIDAR_USEPOLLING=true
backend-dev:
container_name: backend-dev
build:
context: ./backend
dockerfile: Dockerfile
tty: true
ports:
- 5000:5000
environment:
DEBUG: "yes"
PORT: 5050
HOST: "0.0.0.0"

8
frontend/.dockerignore Normal file
View File

@ -0,0 +1,8 @@
node_modules
npm-debug.log
build
.dockerignore
**/.git
**/.DS_Store
**/node_modules
.env

19
frontend/Dockerfile Normal file
View File

@ -0,0 +1,19 @@
FROM node:latest
# set working directory
WORKDIR /app
# add `/app/node_modules/.bin` to $PATH
ENV PATH /app/node_modules/.bin:$PATH
# install app dependencies
COPY package.json ./
COPY package-lock.json ./
RUN npm install
# add app
COPY . ./
# start app
CMD ["npm", "start"]

15
frontend/Dockerfile.prod Normal file
View File

@ -0,0 +1,15 @@
FROM node:latest as build
WORKDIR /app
ENV PATH /app/node_modules/.bin:$PATH
COPY package.json ./
COPY package-lock.json ./
RUN npm ci --silent
COPY . ./
RUN npm run build
FROM nginx:latest
COPY --from=build /app/build /usr/share/nginx/html
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx/nginx.conf /etc/nginx/conf.d
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

70
frontend/README.md Normal file
View File

@ -0,0 +1,70 @@
# Getting Started with Create React App
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
## Available Scripts
In the project directory, you can run:
### `npm start`
Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
The page will reload if you make edits.\
You will also see any lint errors in the console.
### `npm test`
Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `npm run build`
Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `npm run eject`
**Note: this is a one-way operation. Once you `eject`, you cant go back!**
If you arent satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point youre on your own.
You dont have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldnt feel obligated to use this feature. However we understand that this tool wouldnt be useful if you couldnt customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).
### Code Splitting
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
### Analyzing the Bundle Size
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
### Making a Progressive Web App
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
### Advanced Configuration
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
### Deployment
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
### `npm run build` fails to minify
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)

16490
frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

40
frontend/package.json Normal file
View File

@ -0,0 +1,40 @@
{
"name": "letter-creator",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^11.2.7",
"@testing-library/user-event": "^12.8.3",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^5.2.1",
"react-scripts": "4.0.3",
"web-vitals": "^1.1.2"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"proxy": "http://localhost:5000"
}

BIN
frontend/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

BIN
frontend/public/logo192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
frontend/public/logo512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -0,0 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View File

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

View File

@ -0,0 +1,7 @@
node_modules
npm-debug.log
build
.dockerignore
**/.git
**/.DS_Store
**/node_modules

15
frontend/src/App.css Normal file
View File

@ -0,0 +1,15 @@
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: "Computer Modern Serif", serif;
}
:root {
--primary: rgb(44, 133, 72);
--secondary: rgb(218, 218, 218);
}
.letter-adresses {
display: flex;
}

25
frontend/src/App.js Normal file
View File

@ -0,0 +1,25 @@
import "./App.css";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Home from "./components/pages/Home";
import Navbar from "./components/Navbar";
import Footer from "./components/Footer";
import Faq from "./components/pages/Faq";
import About from "./components/pages/About";
function App() {
return (
<>
<Router>
<Navbar />
<Switch>
<Route path="/" exact component={Home} />
<Route path="/faq" component={Faq} />
<Route path="/about" component={About} />
</Switch>
<Footer />
</Router>
</>
);
}
export default App;

8
frontend/src/App.test.js Normal file
View File

@ -0,0 +1,8 @@
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

View File

@ -0,0 +1,75 @@
footer {
position: fixed;
bottom: 0;
width: 100%;
}
.footer-container {
background-color: var(--primary);
padding: 4rem 0 2rem 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.footer-links {
width: 100%;
max-width: 1080px;
display: flex;
justify-content: space-around;
}
.footer-link-wrapper {
display: flex;
}
.footer-link-items {
display: flex;
flex-direction: column;
align-items: left;
text-align: left;
box-sizing: content-box;
}
.footer-link-items h2 {
margin-bottom: 16px;
}
.footer-link-items > h2 {
color: var(--secondary);
}
.footer-link-items a {
color: var(--secondary);
text-decoration: none;
margin-bottom: 8px;
}
.footer-link-items a:hover {
border-bottom: 0.2em dotted var(--secondary);
transition: 0.2s none;
}
@media screen and (max-width: 768px) {
.footer-container {
display: flex;
}
.footer-links {
display: flex;
flex-flow: column wrap;
}
.footer-link-wrapper {
display: flex;
justify-content: space-around;
margin-bottom: 2rem;
}
.footer-link-items {
display: flex;
align-items: center;
flex-direction: column;
}
}

View File

@ -0,0 +1,36 @@
import React from "react";
import { Link } from "react-router-dom";
import "./Footer.css";
function Footer() {
const EMAIL_ADRESS = process.env.REACT_APP_EMAIL_ADRESS;
const GPG_KEY = process.env.REACT_APP_GPG_KEY
return (
<footer className="footer-container">
<div className="footer-links">
<div className="footer-link-wrapper">
<div className="footer-link-items">
<h2>Informationen</h2>
<Link to="/ueber">Hintergrund</Link>
<Link to="/manual">Wie funktioniert das?</Link>
</div>
</div>
<div className="footer-link-wrapper">
<div className="footer-link-items">
<h2>Kontakt</h2>
<Link to={EMAIL_ADRESS}> {EMAIL_ADRESS}</Link>
<Link to={GPG_KEY}>GPG-Key</Link>
</div>
</div>
<div className="footer-link-wrapper">
<div className="footer-link-items">
<h2>Rechtliches</h2>
<Link to="/privacy">Datenschutz</Link>
</div>
</div>
</div>
</footer>
);
}
export default Footer;

View File

View File

@ -0,0 +1,59 @@
.navbar {
background-color: var(--primary);
height: 4rem;
display: flex;
justify-content: center;
align-items: center;
font-size: 1.2rem;
position: sticky;
top: 0;
z-index: 999;
}
.navbar-container {
display: flex;
justify-content: center;
align-items: center;
height: 4rem;
max-width: 1080px;
}
.navbar-logo {
color: var(--secondary);
justify-self: start;
margin-left: 1.5em;
cursor: pointer;
text-decoration: none;
font-size: 2rem;
display: flex;
align-items: center;
}
.nav-menu {
display: grid;
grid-template-columns: repeat(5, auto);
grid-gap: 1rem;
list-style: none;
text-align: center;
width: 60vw;
justify-content: end;
margin-right: 2rem;
}
.nav-item {
height: 4rem;
}
.nav-links {
color: var(--secondary);
display: flex;
align-items: center;
text-decoration: none;
padding: 0.1rem 0.2rem;
height: 100%;
}
.nav-links:hover {
border-bottom: 0.2em dotted var(--secondary);
transition: all 0.2s none;
}

View File

@ -0,0 +1,36 @@
import React from "react";
import { Link } from "react-router-dom";
import "./Navbar.css";
function Navbar() {
return (
<>
<nav className="navbar">
<div className="navbar-container">
<Link to="/" className="navbar-logo">
Briefgenerator{" "}
</Link>
<ul className="nav-menu">
<li className="nav-item">
<Link to="/" className="nav-links">
Startseite
</Link>
</li>
<li className="nav-item">
<Link to="/about" className="nav-links">
Über
</Link>
</li>
<li className="nav-item">
<Link to="/faq" className="nav-links">
FAQ
</Link>
</li>
</ul>
</div>
</nav>
</>
);
}
export default Navbar;

View File

View File

@ -0,0 +1,13 @@
import React from "react";
import "./Input.css";
function SubmitInput(props) {
return (
<label className="submit-field">
<p>{props.label}</p>
<input type="submit" value={props.value} />
</label>
);
}
export default SubmitInput;

View File

@ -0,0 +1,20 @@
import React from "react";
import "../App.css";
function TextAreaInput(props) {
return (
<label className="input-field">
<p>{props.label}</p>
<textarea
value={props.value}
name={props.name}
placeholder={props.value || "Brieftext"}
onChange={props.onChange}
rows={props.rows || 5}
cols={props.cols || 30}
/>
</label>
);
}
export default TextAreaInput;

View File

@ -0,0 +1,18 @@
import React from "react";
import "./Input.css";
function TextInput(props) {
return (
<label className="input-field">
<p>{props.label}</p>
<input
type="text"
name={props.name}
placeholder={props.placeholder}
onChange={props.onChange}
/>
</label>
);
}
export default TextInput;

View File

@ -0,0 +1,8 @@
import React from "react";
import "../../App.css";
function About() {
return <h1>About</h1>;
}
export default About;

View File

@ -0,0 +1,8 @@
import React from "react";
import "../../App.css";
function Faq() {
return <h1>FAQ</h1>;
}
export default Faq;

View File

@ -0,0 +1,169 @@
import React, { useState, useEffect } from "react";
import TextInput from "../TextInput";
import SubmitInput from "../SubmitInput";
import TextAreaInput from "../TextAreaInput";
import "../../App.css";
function Home() {
const [currentTime, setCurrentTime] = useState(0);
const [state, setState] = useState({
senderGender: "",
senderPrename: "",
senderSurname: "",
senderStreet: "",
senderHouse: "",
senderPlz: "",
senderPlace: "",
receiverGender: "",
receiverPrename: "",
receiverSurname: "",
receiverStreet: "",
receiverHouse: "",
receiverPlz: "",
receiverPlace: "",
subject: "",
message: "",
});
const handleChange = (e) => {
const value = e.target.value;
setState({
...state,
[e.target.name]: value,
});
};
const onSubmitClick = (e) => {
e.preventDefault();
let opts = {
state: state
};
fetch("api/generate-letter", {
method: "post",
body: JSON.stringify(opts),
}).then((response) => {
if (response.status !== 200) {
console.log("An error occured");
}
});
};
useEffect(() => {
fetch("/time")
.then((res) => res.json())
.then((data) => {
setCurrentTime(data.time);
});
}, []);
return (
<div className="App">
<form onSubmit={onSubmitClick}>
<div className="letter-adresses">
<div className="adress-sender">
<h2>Absender</h2>
<TextInput
label="Geschlecht"
name="senderGender"
placeholder="Frau"
onChange={handleChange}
/>
<TextInput
label="Vorname"
name="senderPrename"
placeholder="Max"
onChange={handleChange}
/>
<TextInput
label="Nachname"
name="senderSurname"
placeholder="Muster"
onChange={handleChange}
/>
<TextInput
label="Strasse"
name="senderStreet"
placeholder="Musterstrasse"
onChange={handleChange}
/>
<TextInput
label="Hausnummer"
name="senderHouse"
placeholder="12"
onChange={handleChange}
/>
<TextInput
label="PLZ"
name="senderPlz"
placeholder="1234"
onChange={handleChange}
/>
<TextInput
label="Ort"
name="senderPlace"
placeholder="Musterhausen"
onChange={handleChange}
/>
</div>
<div className="adress-receiver">
<h2>Empfänger</h2>
<TextInput
label="Geschlecht"
name="receiverGender"
placeholder="Frau"
onChange={handleChange}
/>
<TextInput
label="Vorname"
name="receiverPrename"
placeholder="Max"
onChange={handleChange}
/>
<TextInput
label="Nachname"
name="receiverSurname"
placeholder="Muster"
onChange={handleChange}
/>
<TextInput
label="Strasse"
name="receiverStreet"
placeholder="Musterstrasse"
onChange={handleChange}
/>
<TextInput
label="Hausnummer"
name="receiverHouse"
placeholder="12"
onChange={handleChange}
/>
<TextInput
label="PLZ"
name="receiverPlz"
placeholder="1234"
onChange={handleChange}
/>
<TextInput
label="Ort"
name="receiverPlace"
placeholder="Musterhausen"
onChange={handleChange}
/>
</div>
</div>
<h2>Nachricht</h2>
<TextInput
label="Betreff"
name="subject"
placeholder="Betreff"
onChange={handleChange}
/>
<TextAreaInput name="message" onChange={handleChange} />
<SubmitInput value="Brief generieren" />
</form>
</div>
);
}
export default Home;

13
frontend/src/index.css Normal file
View File

@ -0,0 +1,13 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

17
frontend/src/index.js Normal file
View File

@ -0,0 +1,17 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
// 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();

1
frontend/src/logo.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,13 @@
const reportWebVitals = onPerfEntry => {
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;

View File

@ -0,0 +1,5 @@
// 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';

9
start.sh Normal file
View File

@ -0,0 +1,9 @@
#!/bin/bash
podman stop frontend-dev
podman stop backend-dev
podman rm frontend-dev
podman rm backend-dev
podman rmi localhost/latex-brief-webseite_backend-dev
podman rmi localhost/latex-brief-webseite_frontend-dev
podman-compose up --build