refactor: Consolidate error pages into single ErrorPage component

- Created generic ErrorPage.js with errorCode prop
- Centralized error messages in ERROR_MESSAGES dictionary
- Updated App.js to use ErrorPage for all error routes
- Updated ErrorBoundary.js to use new ErrorPage component
- Removed duplicate files: 403Page.js, 404Page.js, 500Page.js, 502Page.js, 503Page.js
- Fixed 403/404 routing: protected routes show 403, unknown routes show 404
- Error pages now vertically centered with min-height: 100vh
This commit is contained in:
Matthias Lotz 2025-11-29 12:17:51 +01:00
parent 91d6d06687
commit e4a76a6b3d
12 changed files with 343 additions and 308 deletions

View File

@ -8,11 +8,7 @@ import ErrorBoundary from './Components/ComponentUtils/ErrorBoundary.js';
// Always loaded (public + internal)
import MultiUploadPage from './Components/Pages/MultiUploadPage';
import ManagementPortalPage from './Components/Pages/ManagementPortalPage';
import NotFoundPage from './Components/Pages/404Page.js';
import ForbiddenPage from './Components/Pages/403Page.js';
import InternalServerErrorPage from './Components/Pages/500Page.js';
import BadGatewayPage from './Components/Pages/502Page.js';
import ServiceUnavailablePage from './Components/Pages/503Page.js';
import ErrorPage from './Components/Pages/ErrorPage.js';
// Lazy loaded (internal only) - Code Splitting für Performance
const SlideshowPage = lazy(() => import('./Components/Pages/SlideshowPage'));
@ -23,14 +19,14 @@ const ModerationGroupImagesPage = lazy(() => import('./Components/Pages/Moderati
/**
* Protected Route Component
* Redirects to upload page if accessed from public host
* Shows 403 page if accessed from public host
*/
const ProtectedRoute = ({ children }) => {
const hostConfig = getHostConfig();
if (hostConfig.isPublic) {
// Redirect to upload page - feature not available on public
return <Navigate to="/" replace />;
// Show 403 - feature not available on public
return <ErrorPage errorCode="403" />;
}
return children;
@ -67,14 +63,13 @@ function App() {
<Route path="/manage/:token" element={<ManagementPortalPage />} />
{/* Error Pages */}
<Route path="/error/403" element={<ForbiddenPage />} />
<Route path="/error/500" element={<InternalServerErrorPage />} />
<Route path="/error/502" element={<BadGatewayPage />} />
<Route path="/error/503" element={<ServiceUnavailablePage />} />
<Route path="/error/403" element={<ErrorPage errorCode="403" />} />
<Route path="/error/404" element={<ErrorPage errorCode="404" />} />
<Route path="/error/500" element={<ErrorPage errorCode="500" />} />
<Route path="/error/502" element={<ErrorPage errorCode="502" />} />
<Route path="/error/503" element={<ErrorPage errorCode="503" />} />
{/* Internal Only Routes - nur auf internal host geladen */}
{hostConfig.isInternal && (
<>
{/* Internal Only Routes - geschützt durch ProtectedRoute */}
<Route
path="/slideshow"
element={
@ -115,11 +110,9 @@ function App() {
</ProtectedRoute>
}
/>
</>
)}
{/* 404 / Not Found */}
<Route path="*" element={<NotFoundPage />} />
<Route path="*" element={<ErrorPage errorCode="404" />} />
</Routes>
</Suspense>
</Router>

View File

@ -1,5 +1,5 @@
import React from 'react';
import InternalServerErrorPage from '../Pages/500Page';
import ErrorPage from '../Pages/ErrorPage';
/**
* Error Boundary Component
@ -28,7 +28,7 @@ class ErrorBoundary extends React.Component {
render() {
if (this.state.hasError) {
// Render 500 Error Page
return <InternalServerErrorPage />;
return <ErrorPage errorCode="500" />;
}
return this.props.children;

View File

@ -9,9 +9,9 @@ const Loading = () => {
<div className="loading-logo-container">
<div className="rotor">
<svg
className="loading-logo"
class="loading-logo"
version="1.1"
viewBox="0 0 841.89 595.28"
viewBox="260 90 310 190"
xmlns="http://www.w3.org/2000/svg"
>
<g id="g136" display="inline">

View File

@ -1,30 +0,0 @@
import React from 'react'
import Navbar from '../ComponentUtils/Headers/Navbar'
import NavbarUpload from '../ComponentUtils/Headers/NavbarUpload'
import ErrorAnimation from '../ComponentUtils/ErrorAnimation/ErrorAnimation'
import { getHostConfig } from '../../Utils/hostDetection'
import './Css/ErrorPage.css'
function ForbiddenPage() {
const hostConfig = getHostConfig();
return (
<div className="allContainerNoBackground">
{hostConfig.isPublic ? <NavbarUpload /> : <Navbar />}
<div className="containerError">
<div style={{ textAlign: 'center', marginTop: '2rem' }}>
<h1>403 - Zugriff verweigert</h1>
<p>Sie haben keine Berechtigung, auf diese Ressource zuzugreifen.</p>
<ErrorAnimation errorCode="403" />
<a href="/" style={{ color: '#007bff', textDecoration: 'underline' }}>
Zurück zur Startseite
</a>
</div>
</div>
</div>
)
}
export default ForbiddenPage

View File

@ -1,43 +0,0 @@
import React from 'react'
import Navbar from '../ComponentUtils/Headers/Navbar'
import NavbarUpload from '../ComponentUtils/Headers/NavbarUpload'
import ErrorAnimation from '../ComponentUtils/ErrorAnimation/ErrorAnimation'
import { getHostConfig } from '../../Utils/hostDetection'
import './Css/ErrorPage.css'
import '../../App.css'
function FZF() {
const hostConfig = getHostConfig();
return (
<div className="allContainerNoBackground">
{hostConfig.isPublic ? <NavbarUpload /> : <Navbar />}
<div className="containerError">
{hostConfig.isPublic ? (
<div style={{ textAlign: 'center', marginTop: '2rem' }}>
<h1>404 - Diese Funktion ist nicht verfügbar</h1>
<p>Diese Funktion ist nur über das interne Netzwerk erreichbar.</p>
<ErrorAnimation errorCode="403" />
<a href="/" style={{ color: '#007bff', textDecoration: 'underline' }}>
Zurück zum Upload
</a>
</div>
) : (
<div style={{ textAlign: 'center', marginTop: '2rem' }}>
<h1>404 - Seite nicht gefunden</h1>
<p>Die angeforderte Seite existiert nicht.</p>
<ErrorAnimation errorCode="404" />
<a href="/" style={{ color: '#007bff', textDecoration: 'underline' }}>
Zurück zur Startseite
</a>
</div>
)}
</div>
</div>
)
}
export default FZF

View File

@ -1,30 +0,0 @@
import React from 'react'
import Navbar from '../ComponentUtils/Headers/Navbar'
import NavbarUpload from '../ComponentUtils/Headers/NavbarUpload'
import ErrorAnimation from '../ComponentUtils/ErrorAnimation/ErrorAnimation'
import { getHostConfig } from '../../Utils/hostDetection'
import './Css/ErrorPage.css'
function InternalServerErrorPage() {
const hostConfig = getHostConfig();
return (
<div className="allContainerNoBackground">
{hostConfig.isPublic ? <NavbarUpload /> : <Navbar />}
<div className="containerError">
<div style={{ textAlign: 'center', marginTop: '2rem' }}>
<h1>500 - Interner Serverfehler</h1>
<p>Es ist ein unerwarteter Fehler aufgetreten. Bitte versuchen Sie es später erneut.</p>
<ErrorAnimation errorCode="500" />
<a href="/" style={{ color: '#007bff', textDecoration: 'underline' }}>
Zurück zur Startseite
</a>
</div>
</div>
</div>
)
}
export default InternalServerErrorPage

View File

@ -1,30 +0,0 @@
import React from 'react'
import Navbar from '../ComponentUtils/Headers/Navbar'
import NavbarUpload from '../ComponentUtils/Headers/NavbarUpload'
import ErrorAnimation from '../ComponentUtils/ErrorAnimation/ErrorAnimation'
import { getHostConfig } from '../../Utils/hostDetection'
import './Css/ErrorPage.css'
function BadGatewayPage() {
const hostConfig = getHostConfig();
return (
<div className="allContainerNoBackground">
{hostConfig.isPublic ? <NavbarUpload /> : <Navbar />}
<div className="containerError">
<div style={{ textAlign: 'center', marginTop: '2rem' }}>
<h1>502 - Bad Gateway</h1>
<p>Der Server hat eine ungültige Antwort erhalten. Bitte versuchen Sie es später erneut.</p>
<ErrorAnimation errorCode="502" />
<a href="/" style={{ color: '#007bff', textDecoration: 'underline' }}>
Zurück zur Startseite
</a>
</div>
</div>
</div>
)
}
export default BadGatewayPage

View File

@ -1,30 +0,0 @@
import React from 'react'
import Navbar from '../ComponentUtils/Headers/Navbar'
import NavbarUpload from '../ComponentUtils/Headers/NavbarUpload'
import ErrorAnimation from '../ComponentUtils/ErrorAnimation/ErrorAnimation'
import { getHostConfig } from '../../Utils/hostDetection'
import './Css/ErrorPage.css'
function ServiceUnavailablePage() {
const hostConfig = getHostConfig();
return (
<div className="allContainerNoBackground">
{hostConfig.isPublic ? <NavbarUpload /> : <Navbar />}
<div className="containerError">
<div style={{ textAlign: 'center', marginTop: '2rem' }}>
<h1>503 - Service nicht verfügbar</h1>
<p>Der Service ist vorübergehend nicht verfügbar. Bitte versuchen Sie es später erneut.</p>
<ErrorAnimation errorCode="503" />
<a href="/" style={{ color: '#007bff', textDecoration: 'underline' }}>
Zurück zur Startseite
</a>
</div>
</div>
</div>
)
}
export default ServiceUnavailablePage

View File

@ -1,6 +1,5 @@
/* Error Pages Container */
.containerError {
margin-top: 25vh;
display: flex;
flex-direction: row;
flex-wrap: nowrap;

View File

@ -0,0 +1,56 @@
import React from 'react'
import Navbar from '../ComponentUtils/Headers/Navbar'
import NavbarUpload from '../ComponentUtils/Headers/NavbarUpload'
import ErrorAnimation from '../ComponentUtils/ErrorAnimation/ErrorAnimation'
import { getHostConfig } from '../../Utils/hostDetection'
import './Css/ErrorPage.css'
import '../../App.css'
const ERROR_MESSAGES = {
'403': {
title: '403 - Zugriff verweigert',
description: 'Sie haben keine Berechtigung, auf diese Ressource zuzugreifen.'
},
'404': {
title: '404 - Seite nicht gefunden',
description: 'Die angeforderte Seite existiert nicht.'
},
'500': {
title: '500 - Interner Serverfehler',
description: 'Es ist ein interner Serverfehler aufgetreten.'
},
'502': {
title: '502 - Bad Gateway',
description: 'Der Server hat eine ungültige Antwort erhalten.'
},
'503': {
title: '503 - Service nicht verfügbar',
description: 'Der Service ist vorübergehend nicht verfügbar.'
}
};
function ErrorPage({ errorCode = '404' }) {
const hostConfig = getHostConfig();
const error = ERROR_MESSAGES[errorCode] || ERROR_MESSAGES['404'];
return (
<div className="allContainerNoBackground">
{hostConfig.isPublic ? <NavbarUpload /> : <Navbar />}
<div className="containerError">
<div style={{ textAlign: 'center' }}>
<h1 style={{ textAlign: 'center' }}>{error.title}</h1>
<p>{error.description}</p>
<ErrorAnimation errorCode={errorCode} />
<a href="/" style={{ color: '#007bff', textDecoration: 'underline' }}>
Zurück zur Startseite
</a>
</div>
</div>
</div>
)
}
export default ErrorPage

162
test-error-page.html Normal file
View File

@ -0,0 +1,162 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Loading Animation Test</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Roboto', Arial, sans-serif;
background-color: whitesmoke;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 20px;
}
h1 {
color: #333;
margin-bottom: 40px;
text-align: center;
}
.demo-section {
background: white;
padding: 40px;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
margin-bottom: 30px;
max-width: 800px;
width: 100%;
}
.demo-section h2 {
color: #333;
margin-bottom: 20px;
text-align: center;
font-size: 1.5rem;
}
.demo-description {
color: #666;
text-align: center;
margin-bottom: 30px;
font-size: 0.95rem;
}
/* Loading Animation Styles */
.loading-logo-container {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 400px;
position: relative;
perspective: 1000px;
}
.rotor {
display: inline-block;
transform-origin: center;
transform-style: preserve-3d;
will-change: transform;
animation: rotateY 4s linear infinite;
}
.loading-logo {
display: block;
width: 400px;
height: auto;
}
.loading-logo #g136 {
transform-box: fill-box;
transform-origin: center;
will-change: transform;
animation: rotateHammerAxis 3s linear infinite;
}
@keyframes rotateY {
from {
transform: rotateY(0deg);
}
to {
transform: rotateY(360deg);
}
}
@keyframes rotateHammerAxis {
from {
transform: rotate3d(1, -1, 0, 0deg);
}
to {
transform: rotate3d(1, -1, 0, 360deg);
}
}
</style>
</head>
<body>
<h1>🎨 Loading Animation Test & Fehlerseiten-Design</h1>
<!-- Original Loading Animation -->
<div class="demo-section">
<h2>Original Loading Animation</h2>
<p class="demo-description">Die Standard-Loading-Animation mit grünem Hammer</p>
<div class="loading-logo-container">
<div class="rotor">
<svg
class="loading-logo"
version="1.1"
viewBox="0 0 289.40499 170.09499"
id="svg264"
>
<g id="g561" style="display:inline">
<path id="path1353" style="display:inline;fill:#48484a" d="M 138.80469 0 C 97.587768 0 63.224812 29.321264 55.423828 68.242188 C 53.972832 68.119188 52.50934 68.042969 51.027344 68.042969 C 22.8464 68.042969 0 90.887413 0 119.06836 C 0 147.2483 22.8474 170.0957 51.027344 170.0957 C 65.865314 170.0957 210.51721 170.09375 225.61719 170.09375 C 260.84611 170.09375 289.4043 142.40467 289.4043 107.17773 C 289.4053 71.952807 260.84808 43.392578 225.61914 43.392578 C 221.50914 43.392578 217.49456 43.796064 213.60156 44.539062 C 199.2046 18.011166 171.10863 0 138.80469 0 z M 171.96289 40.238281 A 39.540237 71.54811 46.312638 0 1 192.97852 47.357422 A 39.540237 71.54811 46.312638 0 1 170.08984 124.95117 A 39.540237 71.54811 46.312638 0 1 90.582031 147.28711 A 39.540237 71.54811 46.312638 0 1 113.4707 69.695312 A 39.540237 71.54811 46.312638 0 1 171.96289 40.238281 z "/>
</g>
<g id="g136">
<g id="siebensegment" transform="matrix(0.46393276,-0.46393277,0.46393277,0.46393276,33.958225,228.89983)" style="display:inline">
<g id="g1758" transform="translate(113.66502,-113.03641)">
<polygon points="20,20 10,10 20,0 60,0 70,10 60,20 " id="polygon1573" transform="matrix(0.67523047,0,0,0.67523047,117.69293,49.286325)" style="fill:#76b043;stroke:none;stroke-width:2"/>
<polygon points="60,20 70,10 80,20 80,40 70,50 60,40 " id="polygon1575" transform="matrix(0.67523047,0,0,0.67523047,119.69293,51.286325)" style="fill:#76b043;stroke:none;stroke-width:2"/>
<polygon points="80,60 80,80 70,90 60,80 60,60 70,50 " id="polygon1577" transform="matrix(0.67523047,0,0,0.67523047,119.69293,55.286325)" style="fill:#76b043;stroke:none;stroke-width:2"/>
<polygon points="20,80 60,80 70,90 60,100 20,100 10,90 " id="polygon1579" transform="matrix(0.67523047,0,0,0.67523047,117.69293,57.286325)" style="fill:#76b043;stroke:none;stroke-width:2"/>
<polygon points="10,80 0,90 -10,80 -10,60 0,50 10,60 " id="polygon1581" transform="matrix(0.67523047,0,0,0.67523047,122.44524,55.286325)" style="fill:#76b043;stroke:none;stroke-width:2"/>
<polygon points="10,20 10,40 0,50 -10,40 -10,20 0,10 " id="polygon1583" transform="matrix(0.67523047,0,0,0.67523047,122.44524,51.286325)" style="fill:#76b043;stroke:none;stroke-width:2"/>
<polygon points="20,60 10,50 20,40 60,40 70,50 60,60 " id="polygon1585" transform="matrix(0.67523047,0,0,0.67523047,117.69293,53.286325)" style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2"/>
</g>
<g id="g1782" transform="translate(179.35956,-113.03641)">
<polygon points="70,10 60,20 20,20 10,10 20,0 60,0 " id="polygon1768" transform="matrix(0.67523047,0,0,0.67523047,117.69293,49.286325)" style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2"/>
<polygon points="70,50 60,40 60,20 70,10 80,20 80,40 " id="polygon1770" transform="matrix(0.67523047,0,0,0.67523047,119.69293,51.286325)" style="fill:#76b043;stroke:none;stroke-width:2"/>
<polygon points="60,60 70,50 80,60 80,80 70,90 60,80 " id="polygon1772" transform="matrix(0.67523047,0,0,0.67523047,119.69293,55.286325)" style="fill:#76b043;stroke:none;stroke-width:2"/>
<polygon points="20,100 10,90 20,80 60,80 70,90 60,100 " id="polygon1774" transform="matrix(0.67523047,0,0,0.67523047,117.69293,57.286325)" style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2"/>
<polygon points="0,50 10,60 10,80 0,90 -10,80 -10,60 " id="polygon1776" transform="matrix(0.67523047,0,0,0.67523047,122.44524,55.286325)" style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2"/>
<polygon points="-10,20 0,10 10,20 10,40 0,50 -10,40 " id="polygon1778" transform="matrix(0.67523047,0,0,0.67523047,122.44524,51.286325)" style="fill:#76b043;stroke:none;stroke-width:2"/>
<polygon points="70,50 60,60 20,60 10,50 20,40 60,40 " id="polygon1780" transform="matrix(0.67523047,0,0,0.67523047,117.69293,53.286325)" style="fill:#76b043;stroke:none;stroke-width:2"/>
</g>
<g id="g1800" transform="translate(47.970487,-113.03641)">
<polygon points="60,20 20,20 10,10 20,0 60,0 70,10 " id="polygon1786" transform="matrix(0.67523047,0,0,0.67523047,117.69293,49.286325)" style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2"/>
<polygon points="60,40 60,20 70,10 80,20 80,40 70,50 " id="polygon1788" transform="matrix(0.67523047,0,0,0.67523047,119.69293,51.286325)" style="fill:#76b043;stroke:none;stroke-width:2"/>
<polygon points="70,50 80,60 80,80 70,90 60,80 60,60 " id="polygon1790" transform="matrix(0.67523047,0,0,0.67523047,119.69293,55.286325)" style="fill:#76b043;stroke:none;stroke-width:2"/>
<polygon points="10,90 20,80 60,80 70,90 60,100 20,100 " id="polygon1792" transform="matrix(0.67523047,0,0,0.67523047,117.69293,57.286325)" style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2"/>
<polygon points="10,60 10,80 0,90 -10,80 -10,60 0,50 " id="polygon1794" transform="matrix(0.67523047,0,0,0.67523047,122.44524,55.286325)" style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2"/>
<polygon points="0,10 10,20 10,40 0,50 -10,40 -10,20 " id="polygon1796" transform="matrix(0.67523047,0,0,0.67523047,122.44524,51.286325)" style="fill:#76b043;stroke:none;stroke-width:2"/>
<polygon points="60,60 20,60 10,50 20,40 60,40 70,50 " id="polygon1798" transform="matrix(0.67523047,0,0,0.67523047,117.69293,53.286325)" style="fill:#76b043;stroke:none;stroke-width:2"/>
</g>
</g>
</g>
</svg>
</div>
</div>
</div>
</body>
</html>

View File

@ -52,7 +52,6 @@
font-size: 0.95rem;
}
/* Loading Animation Styles */
.loading-logo-container {
display: flex;
justify-content: center;
@ -63,6 +62,7 @@
perspective: 1000px;
}
/* Äußerer Container: Y-Achsen-Rotation für Wolke UND Hammer zusammen */
.rotor {
display: inline-block;
transform-origin: center;
@ -77,13 +77,15 @@
height: auto;
}
/* Hammer: zusätzliche Rotation um eigene Längsachse */
.loading-logo #g136 {
transform-box: fill-box;
transform-origin: center;
transform-box: fill-box; /* Bezieht sich auf eigene Bounding Box */
transform-origin: center; /* Mittelpunkt der eigenen BBox */
will-change: transform;
animation: rotateHammerAxis 3s linear infinite;
}
/* Y-Achsen-Rotation mit leichter X-Neigung (vermeidet Totpunkt bei 90°) */
@keyframes rotateY {
from {
transform: rotateY(0deg);
@ -93,6 +95,7 @@
}
}
/* Hammer-Rotation um eigene Längsachse (diagonal) */
@keyframes rotateHammerAxis {
from {
transform: rotate3d(1, -1, 0, 0deg);
@ -101,6 +104,7 @@
transform: rotate3d(1, -1, 0, 360deg);
}
}
</style>
</head>
<body>
@ -115,42 +119,26 @@
<svg
class="loading-logo"
version="1.1"
viewBox="0 0 289.40499 170.09499"
id="svg264"
viewBox="260 90 310 190"
xmlns="http://www.w3.org/2000/svg"
>
<g id="g561" style="display:inline">
<path id="path1353" style="display:inline;fill:#48484a" d="M 138.80469 0 C 97.587768 0 63.224812 29.321264 55.423828 68.242188 C 53.972832 68.119188 52.50934 68.042969 51.027344 68.042969 C 22.8464 68.042969 0 90.887413 0 119.06836 C 0 147.2483 22.8474 170.0957 51.027344 170.0957 C 65.865314 170.0957 210.51721 170.09375 225.61719 170.09375 C 260.84611 170.09375 289.4043 142.40467 289.4043 107.17773 C 289.4053 71.952807 260.84808 43.392578 225.61914 43.392578 C 221.50914 43.392578 217.49456 43.796064 213.60156 44.539062 C 199.2046 18.011166 171.10863 0 138.80469 0 z M 171.96289 40.238281 A 39.540237 71.54811 46.312638 0 1 192.97852 47.357422 A 39.540237 71.54811 46.312638 0 1 170.08984 124.95117 A 39.540237 71.54811 46.312638 0 1 90.582031 147.28711 A 39.540237 71.54811 46.312638 0 1 113.4707 69.695312 A 39.540237 71.54811 46.312638 0 1 171.96289 40.238281 z "/>
</g>
<g id="g136">
<g id="siebensegment" transform="matrix(0.46393276,-0.46393277,0.46393277,0.46393276,33.958225,228.89983)" style="display:inline">
<g id="g1758" transform="translate(113.66502,-113.03641)">
<polygon points="20,20 10,10 20,0 60,0 70,10 60,20 " id="polygon1573" transform="matrix(0.67523047,0,0,0.67523047,117.69293,49.286325)" style="fill:#76b043;stroke:none;stroke-width:2"/>
<polygon points="60,20 70,10 80,20 80,40 70,50 60,40 " id="polygon1575" transform="matrix(0.67523047,0,0,0.67523047,119.69293,51.286325)" style="fill:#76b043;stroke:none;stroke-width:2"/>
<polygon points="80,60 80,80 70,90 60,80 60,60 70,50 " id="polygon1577" transform="matrix(0.67523047,0,0,0.67523047,119.69293,55.286325)" style="fill:#76b043;stroke:none;stroke-width:2"/>
<polygon points="20,80 60,80 70,90 60,100 20,100 10,90 " id="polygon1579" transform="matrix(0.67523047,0,0,0.67523047,117.69293,57.286325)" style="fill:#76b043;stroke:none;stroke-width:2"/>
<polygon points="10,80 0,90 -10,80 -10,60 0,50 10,60 " id="polygon1581" transform="matrix(0.67523047,0,0,0.67523047,122.44524,55.286325)" style="fill:#76b043;stroke:none;stroke-width:2"/>
<polygon points="10,20 10,40 0,50 -10,40 -10,20 0,10 " id="polygon1583" transform="matrix(0.67523047,0,0,0.67523047,122.44524,51.286325)" style="fill:#76b043;stroke:none;stroke-width:2"/>
<polygon points="20,60 10,50 20,40 60,40 70,50 60,60 " id="polygon1585" transform="matrix(0.67523047,0,0,0.67523047,117.69293,53.286325)" style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2"/>
</g>
<g id="g1782" transform="translate(179.35956,-113.03641)">
<polygon points="70,10 60,20 20,20 10,10 20,0 60,0 " id="polygon1768" transform="matrix(0.67523047,0,0,0.67523047,117.69293,49.286325)" style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2"/>
<polygon points="70,50 60,40 60,20 70,10 80,20 80,40 " id="polygon1770" transform="matrix(0.67523047,0,0,0.67523047,119.69293,51.286325)" style="fill:#76b043;stroke:none;stroke-width:2"/>
<polygon points="60,60 70,50 80,60 80,80 70,90 60,80 " id="polygon1772" transform="matrix(0.67523047,0,0,0.67523047,119.69293,55.286325)" style="fill:#76b043;stroke:none;stroke-width:2"/>
<polygon points="20,100 10,90 20,80 60,80 70,90 60,100 " id="polygon1774" transform="matrix(0.67523047,0,0,0.67523047,117.69293,57.286325)" style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2"/>
<polygon points="0,50 10,60 10,80 0,90 -10,80 -10,60 " id="polygon1776" transform="matrix(0.67523047,0,0,0.67523047,122.44524,55.286325)" style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2"/>
<polygon points="-10,20 0,10 10,20 10,40 0,50 -10,40 " id="polygon1778" transform="matrix(0.67523047,0,0,0.67523047,122.44524,51.286325)" style="fill:#76b043;stroke:none;stroke-width:2"/>
<polygon points="70,50 60,60 20,60 10,50 20,40 60,40 " id="polygon1780" transform="matrix(0.67523047,0,0,0.67523047,117.69293,53.286325)" style="fill:#76b043;stroke:none;stroke-width:2"/>
</g>
<g id="g1800" transform="translate(47.970487,-113.03641)">
<polygon points="60,20 20,20 10,10 20,0 60,0 70,10 " id="polygon1786" transform="matrix(0.67523047,0,0,0.67523047,117.69293,49.286325)" style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2"/>
<polygon points="60,40 60,20 70,10 80,20 80,40 70,50 " id="polygon1788" transform="matrix(0.67523047,0,0,0.67523047,119.69293,51.286325)" style="fill:#76b043;stroke:none;stroke-width:2"/>
<polygon points="70,50 80,60 80,80 70,90 60,80 60,60 " id="polygon1790" transform="matrix(0.67523047,0,0,0.67523047,119.69293,55.286325)" style="fill:#76b043;stroke:none;stroke-width:2"/>
<polygon points="10,90 20,80 60,80 70,90 60,100 20,100 " id="polygon1792" transform="matrix(0.67523047,0,0,0.67523047,117.69293,57.286325)" style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2"/>
<polygon points="10,60 10,80 0,90 -10,80 -10,60 0,50 " id="polygon1794" transform="matrix(0.67523047,0,0,0.67523047,122.44524,55.286325)" style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2"/>
<polygon points="0,10 10,20 10,40 0,50 -10,40 -10,20 " id="polygon1796" transform="matrix(0.67523047,0,0,0.67523047,122.44524,51.286325)" style="fill:#76b043;stroke:none;stroke-width:2"/>
<polygon points="60,60 20,60 10,50 20,40 60,40 70,50 " id="polygon1798" transform="matrix(0.67523047,0,0,0.67523047,117.69293,53.286325)" style="fill:#76b043;stroke:none;stroke-width:2"/>
</g>
<g id="g136" display="inline">
<path
display="inline"
fill="#76b043"
d="m 386.456,248.659 c -0.824,0.825 -2.157,0.825 -2.987,0 L 365.572,230.76 c -0.818,-0.816 -0.818,-2.136 -0.005,-2.962 0.005,-0.008 0.005,-0.011 0.011,-0.019 0.006,-0.002 0.01,-0.002 0.017,-0.01 l 52.177,-52.177 20.877,20.876 z"
/>
<path
display="inline"
fill="#76b043"
d="m 473.015,185.95 c -0.021,0.018 -0.025,0.045 -0.043,0.063 -0.02,0.02 -0.045,0.022 -0.064,0.041 l -17.811,17.813 c -0.018,0.019 -0.023,0.046 -0.041,0.061 -0.02,0.02 -0.045,0.026 -0.064,0.045 -0.815,0.758 -2.064,0.754 -2.877,-0.012 -0.012,-0.014 -0.035,-0.018 -0.047,-0.033 -0.012,-0.012 -0.019,-0.033 -0.032,-0.046 l -49.265,-49.265 c -0.014,-0.016 -0.035,-0.02 -0.048,-0.034 -0.013,-0.011 -0.019,-0.034 -0.032,-0.049 -0.783,-0.826 -0.779,-2.121 0.032,-2.929 0.31,-0.312 0.698,-0.465 1.093,-0.543 l 0.004,-0.039 30.859,-5.149 0.035,0.034 c 0.607,-0.061 1.232,0.107 1.704,0.578 l 36.547,36.548 c 0.808,0.811 0.819,2.087 0.05,2.916"
/>
</g>
<g id="g561" display="inline">
<path
fill="#48484a"
d="m 501.16,142.979 c -4.11,0 -8.124,0.403 -12.017,1.146 -14.397,-26.528 -42.494,-44.539 -74.798,-44.539 -41.217,0 -75.58,29.322 -83.381,68.243 -1.451,-0.123 -2.914,-0.2 -4.396,-0.2 -28.181,0 -51.027,22.845 -51.027,51.026 0,28.18 22.847,51.026 51.027,51.026 14.838,0 159.491,-10e-4 174.591,-10e-4 35.229,0 63.787,-27.689 63.787,-62.916 10e-4,-35.225 -28.557,-63.785 -63.786,-63.785 M 386.432,248.707 c -0.824,0.825 -2.157,0.825 -2.987,0 l -17.897,-17.899 c -0.818,-0.816 -0.818,-2.136 -0.005,-2.962 0.005,-0.008 0.005,-0.011 0.011,-0.019 0.006,-0.002 0.01,-0.002 0.017,-0.01 l 52.177,-52.177 20.877,20.876 z m 86.558,-62.709 c -0.021,0.018 -0.025,0.045 -0.043,0.063 -0.02,0.02 -0.045,0.022 -0.064,0.041 l -17.811,17.813 c -0.018,0.019 -0.023,0.046 -0.041,0.061 -0.02,0.02 -0.045,0.026 -0.064,0.045 -0.815,0.758 -2.064,0.754 -2.877,-0.012 -0.012,-0.014 -0.035,-0.018 -0.047,-0.033 -0.012,-0.012 -0.019,-0.033 -0.032,-0.046 l -49.265,-49.265 c -0.014,-0.016 -0.035,-0.02 -0.048,-0.034 -0.013,-0.011 -0.019,-0.034 -0.032,-0.049 -0.783,-0.826 -0.779,-2.121 0.032,-2.929 0.31,-0.312 0.698,-0.465 1.093,-0.543 l 0.004,-0.039 30.859,-5.149 0.035,0.034 c 0.607,-0.061 1.232,0.107 1.704,0.578 l 36.547,36.548 c 0.809,0.811 0.82,2.087 0.05,2.916"
/>
</g>
</svg>
</div>