Compare commits

...

12 Commits

Author SHA1 Message Date
ad515c5be9
Suppress eslint explicit any warnings required for the advanced type trickery to work.
At the call site the types are correct, it is just the type signatures that require any.
2025-04-11 00:53:38 +02:00
c1647ab498
Basic i18n support
Completely TypeScript based translation with support for parameters.
Define the value in messages.ts as a function with arguments, and they will be required at the call site.
Have the full power of TypeScript in the translation so it is possible to do stuff like switching over numbers to write nicer messages for 0/1/n, and so on.
2025-04-11 00:34:55 +02:00
f06a7381e7
useFetch hook (with optional view transition)
Had to use a .d.ts and .js file to get all the types to align and make all the tools happy, IDE/eslint/TypeScript.
2025-04-06 17:38:51 +02:00
6542cee415
Fix for browsers that do not support view transitions 2025-04-04 13:05:38 +02:00
2258f67b54
Add basic routing
Everything sould be set up to start adding features
2025-04-04 13:05:38 +02:00
a53ec6a3fe
Basic splash screen while loading initial data 2025-04-04 13:05:38 +02:00
a8889a231a
Connect frontend and backend 2025-04-03 09:55:52 +02:00
525e76cd5b
Generate OpenAPI specification 2025-04-03 09:55:52 +02:00
33988953d5
Add prettier 2025-04-03 09:55:52 +02:00
843ac8f76d
npm audit fix 2025-04-03 09:55:52 +02:00
cd9660c0b1
Protected backend with OAuth 2 login
On unauthorized HTTP response navigate to the URL indicated by the X-Authorization-Url header to begin the process of logging in.
2025-04-03 09:55:51 +02:00
f60f9f5ac5
npm create vite@latest 2025-04-03 09:55:51 +02:00
49 changed files with 4998 additions and 0 deletions

@ -17,3 +17,13 @@ services:
MOCK_FILE_PATH: /mocks
volumes:
- ./src/mock-api:/mocks
oauth2:
build: https://gitea.dsv.su.se/DMC/oauth2-authorization-server.git
restart: unless-stopped
ports:
- '63164:8080'
environment:
CLIENT_ID: studentportalen
CLIENT_SECRET: p4ssw0rd
CLIENT_REDIRECT_URI: http://localhost:8080/login/oauth2/code/studentportalen
CLIENT_SCOPES: openid profile email offline_access

@ -18,6 +18,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>24</java.version>
<springdoc.version>2.8.6</springdoc.version>
</properties>
<dependencies>
@ -25,6 +26,17 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<!-- OpenAPI specification -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc.version}</version>
</dependency>
</dependencies>
<build>

@ -0,0 +1,12 @@
package se.su.dsv.studentportalen.bff;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("se.su.dsv.frontend")
public record FrontendConfiguration(String url) {
public FrontendConfiguration {
if (url == null || url.isBlank()) {
throw new IllegalArgumentException("se.su.dsv.frontend.url must not be null or blank");
}
}
}

@ -5,12 +5,64 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatchers;
import org.springframework.web.cors.CorsConfiguration;
import se.su.dsv.studentportalen.bff.login.BFFAuthenticationEntryPoint;
import java.util.List;
import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher;
@SpringBootApplication
@EnableConfigurationProperties
@ConfigurationPropertiesScan
public class Studentportalen extends SpringBootServletInitializer {
private static final RequestMatcher DOCUMENTATION_MATCHER = RequestMatchers.anyOf(
antMatcher("/swagger"),
antMatcher("/swagger-ui/**"),
antMatcher("/v3/api-docs/**"));
public static void main(String[] args) {
SpringApplication.run(Studentportalen.class, args);
}
@Bean
public SecurityFilterChain securityFilterChain(
HttpSecurity http,
FrontendConfiguration frontendConfiguration)
throws Exception
{
http.exceptionHandling(exception -> exception
.authenticationEntryPoint(new BFFAuthenticationEntryPoint()));
http.oauth2Login(login -> login
.defaultSuccessUrl(frontendConfiguration.url(), true));
http.authorizeHttpRequests(authorize -> authorize
.requestMatchers(DOCUMENTATION_MATCHER).permitAll()
.anyRequest().authenticated());
http.cors(cors -> cors
.configurationSource(_ -> frontendOnlyCors(frontendConfiguration)));
return http.build();
}
private static CorsConfiguration frontendOnlyCors(FrontendConfiguration frontendConfiguration) {
var corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedOrigins(List.of(frontendConfiguration.url()));
corsConfiguration.setAllowedMethods(List.of("GET", "POST"));
// Allow the frontend to see the X-Authorization-Url header
corsConfiguration.setExposedHeaders(List.of("X-Authorization-Url"));
// To allow the session cookie to be included
corsConfiguration.setAllowCredentials(true);
// Content-Type is allowed by default but with a restriction on the value
// The restriction does not allow "application/json" so we add it as an allowed header
corsConfiguration.setAllowedHeaders(List.of("Content-Type"));
return corsConfiguration;
}
}

@ -0,0 +1,7 @@
/// Everything related to the API used by the frontend
@NonNullApi
@NonNullFields
package se.su.dsv.studentportalen.bff.frontend;
import org.springframework.lang.NonNullApi;
import org.springframework.lang.NonNullFields;

@ -0,0 +1,20 @@
package se.su.dsv.studentportalen.bff.frontend.profile;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Objects;
public record Profile(
@JsonProperty(value = "name", required = true) String name,
@JsonProperty(value = "language", required = true) Language language)
{
public enum Language {
@JsonProperty("sv") SWEDISH,
@JsonProperty("en") ENGLISH
}
public Profile {
Objects.requireNonNull(name, "name must be specified");
Objects.requireNonNull(language, "language must be specified");
}
}

@ -0,0 +1,19 @@
package se.su.dsv.studentportalen.bff.frontend.profile;
import org.springframework.http.MediaType;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public class ProfileController {
@GetMapping("/profile")
public Profile getProfile(
@AuthenticationPrincipal(errorOnInvalidType = true) OAuth2User currentUser)
{
return new Profile(currentUser.getAttribute("name"), Profile.Language.ENGLISH);
}
}

@ -0,0 +1,19 @@
package se.su.dsv.studentportalen.bff.login;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
public class BFFAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) {
String loginUri = ServletUriComponentsBuilder.fromRequest(request)
.replacePath("/oauth2/authorization/studentportalen")
.build()
.toUriString();
response.addHeader("X-Authorization-Url", loginUri);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
}

@ -0,0 +1,17 @@
/// This package contains the classes and logic for handling the login process
/// described in section 6.1 of [OAuth 2.0 for Browser-Based Applications](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps#name-backend-for-frontend-bff).
///
/// The frontend running in the browser will attempt to make a request to the
/// backend-for-frontend (BFF). If the client is not already authenticated (by
/// checking the session cookie), the [se.su.dsv.studentportalen.bff.login.BFFAuthenticationEntryPoint]
/// will respond sending a `401 Unauthorized` response with the header
/// `X-Authorization-Url` set to where the frontend can begin the authentication
/// flow. This initial request corresponds to the request labelled (B) in
/// [figure 1](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps#figure-1).
/// The frontend will then do a full browser navigation to the provided URL (C)
/// which triggers the OAuth 2.0 authorization code flow.
///
/// The entire flow is then performed by Spring Security (D, E, F, and G).
/// Once complete, the user is sent back to the frontend (H) with a valid
/// session.
package se.su.dsv.studentportalen.bff.login;

@ -3,3 +3,18 @@ se:
dsv:
backend-api:
daisy-url: http://localhost:63163/daisy
frontend:
url: http://localhost:5173
spring.security.oauth2.client:
provider:
dsv:
issuer-uri: http://localhost:63164
registration:
studentportalen:
client-id: studentportalen
client-secret: p4ssw0rd
# Lift the restrictions imposed by __Host- prefix during development
# Ideally we keep it on, but it breaks in Chromium on Linux
server.servlet.session.cookie.name: studentportalen-bff-session

@ -1,3 +1,33 @@
# The following properties need to be set in each specific environment
spring.security.oauth2.client.provider.dsv.issuer-uri: ...
spring.security.oauth2.client.registration.studentportalen.client-id: ...
spring.security.oauth2.client.registration.studentportalen.client-secret: ...
se.su.dsv.backend-api.daisy-url: ...
se.su.dsv.frontend.url: ...
# Common properties for all environments
spring:
application:
name: studentportalen-bff
security.oauth2.client:
registration:
studentportalen:
provider: dsv
scope:
- openid
- profile
- email
- offline_access
server.servlet.session:
cookie:
# See https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps#section-6.1.3.2
secure: true
http-only: true
same-site: strict
path: /
name: __Host-JSESSIONID
springdoc:
swagger-ui:
path: /swagger

1
frontend/.env Normal file

@ -0,0 +1 @@
VITE_BACKEND_URL=http://localhost:8080/

24
frontend/.gitignore vendored Normal file

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

1
frontend/.prettierignore Normal file

@ -0,0 +1 @@
src/lib/api.d.ts

1
frontend/.prettierrc Normal file

@ -0,0 +1 @@
{}

14
frontend/README.md Normal file

@ -0,0 +1,14 @@
# Studentportalen frontend
## Developing
### Prerequisites
- Node v20
- npm
Run `npm install` to install all dependencies then run `npm run dev` to start the development server.
Run `npm run format` and `npm run lint` to format and lint the code.
Run `npm run update-api` after having started the BFF to update the typed API client.

33
frontend/eslint.config.js Normal file

@ -0,0 +1,33 @@
import js from "@eslint/js";
import globals from "globals";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import tseslint from "typescript-eslint";
import eslintConfigPrettier from "eslint-config-prettier/flat";
export default tseslint.config(
{ ignores: ["dist"] },
{
extends: [
js.configs.recommended,
...tseslint.configs.recommended,
eslintConfigPrettier,
],
files: ["**/*.{ts,tsx}"],
languageOptions: {
ecmaVersion: 2023,
globals: globals.browser,
},
plugins: {
"react-hooks": reactHooks,
"react-refresh": reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
"react-refresh/only-export-components": [
"warn",
{ allowConstantExport: true },
],
},
},
);

14
frontend/index.html Normal file

@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<link rel="preload" href="/src/assets/SU_logo_optimized.svg" as="image" />
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

3248
frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

36
frontend/package.json Normal file

@ -0,0 +1,36 @@
{
"name": "frontend",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"format": "prettier . --write",
"update-api": "openapi-typescript http://localhost:8080/v3/api-docs --output src/lib/api.d.ts",
"preview": "vite preview"
},
"dependencies": {
"openapi-fetch": "^0.13.5",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-router": "^7.4.1"
},
"devDependencies": {
"@eslint/js": "^9.21.0",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"@vitejs/plugin-react-swc": "^3.8.0",
"eslint": "^9.21.0",
"eslint-config-prettier": "^10.1.1",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-react-refresh": "^0.4.19",
"globals": "^15.15.0",
"openapi-typescript": "^7.6.1",
"prettier": "3.5.3",
"typescript": "~5.7.2",
"typescript-eslint": "^8.24.1",
"vite": "^6.2.0"
}
}

1
frontend/public/vite.svg Normal file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

(image error) Size: 1.5 KiB

14
frontend/src/App.css Normal file

@ -0,0 +1,14 @@
#app {
height: 100vh;
background-color: var(--color-su-primary);
display: flex;
justify-content: center;
flex-direction: column;
text-align: center;
color: white;
padding: 1em;
align-items: center;
}
#app > img {
max-width: 30em;
}

37
frontend/src/App.tsx Normal file

@ -0,0 +1,37 @@
import "./App.css";
import suLogoLandscape from "./assets/SU_logo_optimized.svg";
import { ProfileContext } from "./hooks/profile.ts";
import Studentportalen from "./Studentportalen.tsx";
import { useViewTransitioningFetch } from "./hooks/fetch";
function App() {
const { data: profile, error } = useViewTransitioningFetch("/profile");
if (!profile) {
return splashScreen("Loading...");
}
if (error) {
return splashScreen("Application failed to start");
}
return (
<ProfileContext value={profile}>
<Studentportalen />
</ProfileContext>
);
}
function splashScreen(extraContent: string) {
return (
<div id={"app"}>
<img src={suLogoLandscape} alt={"Stockholm University"} />
<div>
<h1>Student portal</h1>
<h2>Department of Computer and Systems Sciences</h2>
{extraContent}
</div>
</div>
);
}
export default App;

@ -0,0 +1,17 @@
import { BrowserRouter, Route, Routes } from "react-router";
import Home from "./studentportalen/Home.tsx";
import Layout from "./studentportalen/Layout.tsx";
export default function Studentportalen() {
return (
<>
<BrowserRouter>
<Routes>
<Route element={<Layout />}>
<Route index element={<Home />} />
</Route>
</Routes>
</BrowserRouter>
</>
);
}

File diff suppressed because one or more lines are too long

After

(image error) Size: 31 KiB

@ -0,0 +1,379 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Logotyper" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 671.54 283.1" style="enable-background:new 0 0 671.54 283.1;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g>
<g id="Group_33" transform="translate(-337.004 -221)">
<g id="Group_27" transform="translate(143.967)">
<path id="Path_1187" class="st0" d="M388.85,289.02c0.14,0.16,0.07,0.27-0.08,0.4c-0.27,0.22-1.77,2.43-1.95,2.7
c-0.11,0.18-0.13-0.14-0.07-0.32c0.51-1.48,0.08-3.13-1.08-4.18c-1.21-0.92-2.93-0.68-3.84,0.53c-0.01,0.02-0.03,0.04-0.04,0.06
c-0.94,1.4-0.97,2.67,0,6.33c1.01,3.81,0.59,5.21-0.8,6.83c-1.87,1.85-4.82,2.05-6.92,0.47c-2.71-1.89-2.82-2.32-3.24-2.49
c-0.33-0.14-0.32-0.4,0-0.8c0.48-0.57,2.09-2.75,2.36-3.16c0.18-0.28,0.22,0.04,0.03,0.45c-0.84,1.64-0.4,3.64,1.05,4.77
c1.18,1.1,3.03,1.03,4.13-0.15c0.06-0.07,0.12-0.14,0.18-0.21c0.8-0.96,1.27-1.89,0.14-6.26c-1.2-4.61-0.37-6.12,1.37-7.48
c1.79-1.46,4.37-1.44,6.14,0.04c0.7,0.55,1.35,1.14,1.97,1.78c0.22,0.22,0.34,0.46,0.46,0.57S388.78,288.93,388.85,289.02z"/>
<path id="Path_1188" class="st0" d="M244.24,341.13c0.06-0.16,0.24-0.08,0.47,0c0.95,0.26,1.92,0.43,2.9,0.51
c0.49,0-0.34,0.37-0.59,0.4c-0.69,0.09-2.05,0.49-2.67,2.77c-0.42,1.3,0.29,2.69,1.59,3.11c0.07,0.02,0.13,0.04,0.2,0.06
c2,0.4,3.06-0.36,5.99-2.72c1.78-1.92,4.44-2.75,6.99-2.19c2.41,0.86,3.79,3.4,3.2,5.89c-0.27,1.31-0.79,2.56-1.54,3.68
c-0.12,0.34-0.28,0.46-0.78,0.32c-0.64-0.18-3.05-0.53-3.48-0.58c-0.78-0.09-0.45-0.4,0-0.44c1.24-0.16,3.94-0.5,4.48-3.15
c0.36-1.38-0.47-2.78-1.84-3.14c-0.08-0.02-0.17-0.04-0.25-0.05c-1.23-0.3-2.57-0.26-6.03,2.61c-3.73,3.09-5.32,3.08-7.33,2.17
c-2.1-0.92-3.19-3.26-2.55-5.47c0.21-1.14,0.59-2.24,1.13-3.26C244.24,341.52,244.2,341.24,244.24,341.13z"/>
<path id="Path_1189" class="st0" d="M358.57,449.92c-0.26,0-0.21-0.22-0.25-0.4c0,0-0.66-2.19-0.72-2.37
c-0.06-0.17,0.13-0.24,0.26-0.08c0.95,1.05,2.39,1.51,3.77,1.2c1.48-0.35,2.41-1.82,2.09-3.31c-0.52-1.6-2.09-2.35-5.53-3.93
c-3.1-1.43-4.1-2.86-4.4-4.64c-0.26-2.68,1.41-5.17,4-5.95c2.59-0.8,3.2-0.72,3.72-0.98c0.4-0.21,0.45,0.61,0.5,0.76
c0.25,0.69,0.92,2.84,1.08,3.24c0.16,0.4-0.14,0.51-0.33,0.1c-0.89-1.57-2.69-2.38-4.45-2c-2.31,0.75-2.65,2.4-2.33,3.49
c0.36,1.2,0.57,2.17,4.92,4c4.23,1.78,5.06,3.6,5.08,5.65c-0.14,2.34-1.88,4.28-4.2,4.66c-0.68,0.18-1.38,0.28-2.09,0.31
c-0.31,0.05-0.55,0.07-0.71,0.09C358.82,449.78,358.78,449.92,358.57,449.92z"/>
<path id="Path_1190" class="st0" d="M390.91,293.8c0.62-0.27,1.21-0.61,1.74-1.03c0.22-0.18,0.64,0,0.9,0.26
c0.17,0.17,3.95,3.91,4.69,4.71c1.01,1.07,4.36,4.15,4.79,4.57c0.43,0.42,0.84,0.64,0.4,0.93c-0.44,0.28-1.36,0.8-1.64,0.95
c-0.28,0.15-0.19,0.03-0.19-0.28c0-0.32-0.22-0.8-1.16-1.77c-0.94-0.97-2.02-2.1-2.22-2.26c-0.2-0.16-0.27-0.19-0.44-0.04
c-0.27,0.24-3.97,4-4.79,4.86c-2.07,1.94-3.99,4.04-5.74,6.27c-0.15,0.77,0.1,0.93,0.2,1.07c0.1,0.14,0.22,0.77-0.02,0.53
c-0.24-0.24-1.79-1.93-2.15-2.27c-0.36-0.34-1.87-1.6-2.09-1.81s-0.06-0.32,0.31-0.17c0.51,0.29,1.13,0.3,1.65,0.03
c2.07-1.66,4.02-3.48,5.83-5.42c0.59-0.59,4.6-4.77,4.71-4.89c0.18-0.2,0.25-0.3,0-0.62c-1.02-1.07-2.11-2.08-3.25-3.02
c-0.32-0.26-0.72-0.41-1.13-0.43C390.98,293.97,390.56,293.96,390.91,293.8z"/>
<path id="Path_1191" class="st0" d="M253.1,323.87c-0.35-0.4-1.2-1.11-1.67-1.51c-0.22-0.18-0.1-0.42,0.1-0.74
c0.12-0.2,2.77-5.07,3.38-5.99c0.8-1.24,3.2-5.23,3.49-5.75c0.3-0.52,0.52-0.91,0.77-0.68c0.43,0.43,0.83,0.9,1.2,1.39
c0.21,0.3,0.09,0.15-0.22,0.15s-0.72,0.63-1.45,1.77c-0.52,0.83-0.99,1.68-1.41,2.56c-0.15,0.32,0.06,0.4,0.24,0.54
c0.31,0.2,4.69,2.91,5.73,3.52c2.4,1.48,4.93,2.75,7.54,3.8c0.8-0.04,0.78-0.17,0.92-0.27c0.22-0.16,0.67-0.23,0.45,0
c-0.58,0.75-1.1,1.53-1.57,2.35c-0.37,0.66-1.22,2.24-1.33,2.49s-0.12-0.16-0.1-0.5c0.02-0.4,0.27-0.77-0.33-1.26
c-1.06-0.87-6.1-3.82-6.79-4.25c-0.69-0.43-5.63-3.22-5.99-3.34c-0.26-0.09-0.47-0.4-0.67-0.03c-0.88,1.36-1.63,2.8-2.23,4.31
c-0.09,0.33-0.09,0.69-0.02,1.02C253.25,323.89,253.27,324.05,253.1,323.87z"/>
<path id="Path_1192" class="st0" d="M275.67,294.27c-0.24-0.67-0.56-1.31-0.94-1.91c-0.16-0.32-0.17-0.31,0.26-0.66
c0.19-0.15,4.6-3.41,5.47-4.08c1.2-0.92,5.25-3.65,5.73-4c0.48-0.35,1.02-0.56,1.2-0.12c0.18,0.44,0.66,1.63,0.75,1.93
c0.12,0.44-0.06,0.11-0.38-0.16c-0.24-0.22-0.68-0.18-1.81,0.58c-1.13,0.76-2.71,1.8-2.92,2s-0.18,0.2-0.08,0.4
c0.25,0.5,3.16,4.77,3.98,5.85c1.65,2.43,3.48,4.74,5.46,6.91c0.3,0.15,0.65,0.16,0.96,0.04c0.24-0.05,0.53,0.03,0.13,0.26
c-0.4,0.23-1.6,1.04-2.04,1.33c-0.44,0.29-1.57,1.16-1.96,1.51c-0.47,0.4-0.34-0.03-0.13-0.31c0.21-0.28,0.36-0.46,0.06-1.16
c-1.28-2.28-2.7-4.49-4.25-6.6c-0.48-0.68-4.31-5.8-4.56-6.13c-0.14-0.18-0.18-0.24-0.52,0s-3.32,2.51-3.76,2.92
c-0.34,0.33-0.52,0.4-0.56,0.84C275.74,293.96,275.75,294.6,275.67,294.27z"/>
<path id="Path_1193" class="st0" d="M410.65,311.56c3.75,3.82,3.69,9.95-0.13,13.7c-0.18,0.18-0.38,0.36-0.58,0.52
c-3.91,3.33-9.72,3.13-13.38-0.47c-3.67-3.9-3.49-10.03,0.4-13.7c3.66-3.79,9.7-3.9,13.5-0.24
C410.52,311.44,410.59,311.5,410.65,311.56z M399.28,313.9c-3.31,2.4-4.04,7.03-1.63,10.34c0.05,0.07,0.1,0.13,0.15,0.2
c3.26,2.47,7.91,1.89,10.46-1.32c3.34-2.57,3.97-7.35,1.4-10.69c-0.1-0.13-0.2-0.26-0.31-0.38
C407.5,310.43,403.39,309.9,399.28,313.9L399.28,313.9z"/>
<path id="Path_1194" class="st0" d="M416.12,403.32c-1.72,4.99-7.17,7.65-12.16,5.92c-0.34-0.12-0.67-0.25-0.99-0.41
c-4.51-2.07-6.79-7.17-5.31-11.91c1.8-4.95,7.26-7.52,12.23-5.75C414.94,392.85,417.72,398.25,416.12,403.32z M408.98,394.02
c-3.48-1.91-7.85-0.64-9.77,2.84c-0.08,0.14-0.15,0.29-0.22,0.44c-0.86,2.53,0.14,6.33,5.74,8.43c5.43,2.03,9.51,0,10.44-3.64
C415.52,400.73,415.6,396.39,408.98,394.02z"/>
<path id="Path_1195" class="st0" d="M409.07,347.58c-0.82-0.03-1.64-0.12-2.45-0.26c-0.56-0.12-0.64-0.13-0.8-0.36
c-1.62-2.1-2.51-4.68-2.51-7.34c-0.37-4.49,2.45-8.63,6.76-9.95c3.84-1.05,7.94,0.33,10.36,3.5c1.34,1.9,2.1,4.15,2.17,6.47
c0.09,1.37,0.21,2.19,0.27,2.63c0.12,0.74,0.11,0.94,0.27,1.14c0.16,0.2-0.07,0.4-0.35,0.4s-1.36,0.18-1.91,0.31
c-0.4,0.1-1.25,0.33-1.36,0.36c-0.59,0.14-0.57,0-0.11-0.36c1-0.52,1.69-1.48,1.88-2.59c0.49-2.57-0.22-5.22-1.94-7.19
c-2.32-2.4-5.94-3-8.92-1.49c-3.3,1.24-5.52,4.36-5.59,7.89c-0.18,2.79,1.47,5.38,4.08,6.39
C409.54,347.37,409.9,347.56,409.07,347.58z"/>
<path id="Path_1196" class="st0" d="M424.46,349.87c0,0.72,0.33,3.97,0.46,4.6c0.16,0.76-0.18,0.4-0.22,0.14
c-0.12-0.42-0.48-0.72-0.91-0.76c-2.65,0.03-5.3,0.23-7.92,0.59c2.18,1.95,4.47,3.76,6.88,5.43c0.54,0.45,1.15,0.8,1.8,1.04
c0.17,0,0.08,0.13,0.16-0.09c0.1-0.3,0.4-1.27,0.36,0.2c0,0.35-0.03,1.41,0,1.68c0.03,0.27,0.13,1.01,0.15,1.17
c0.11,0.8-0.02,0.63-0.61-0.09c-1.04-1.08-2.18-2.04-3.42-2.88c-0.65-0.47-4.77-3.6-4.77-3.6s-5.92,5.62-7.04,6.85
c-0.88,0.88-1.65,1.86-2.31,2.92c-0.19,0.43-0.35,0.64-0.28,0.23c0.05-0.26,0.23-1.88,0.27-2.59c0.04-0.62,0.1-2.54,0.11-2.73
c0.02-0.4,0.18-0.1,0.4-0.04c1.42-0.9,2.7-2.01,3.81-3.28c1.39-1.37,3.7-3.88,3.88-4.12c-1.93,0.13-3.85,0.36-5.75,0.68
c-0.73,0.09-1.43,0.34-2.05,0.74c-0.22,0.2-0.36,0.46-0.4,0.76c-0.03,0.24-0.26,0.35-0.3,0.04c-0.04-0.32-0.11-2-0.15-2.6
c-0.04-0.6-0.24-2-0.28-2.2c-0.04-0.2,0.16-0.36,0.28-0.08c0.08,0.27,0.29,0.47,0.55,0.55c0.66,0.26,1.55,0.27,8.08-0.5
c2.59-0.18,5.17-0.55,7.7-1.11c0.69-0.18,1.12-0.44,1.2-1.02C424.18,349.47,424.48,348.81,424.46,349.87z"/>
<path id="Path_1197" class="st0" d="M424.58,372.31c-0.13,0.59-0.66,3.81-0.66,4.46c0,0.65-0.3,0.66-0.38,0.22
c-0.02-0.38-0.26-0.72-0.61-0.87c-1.04-0.43-2.13-0.7-3.25-0.8c-2.26-0.36-3.74-0.62-3.74-0.62s-0.7,2.98-1.09,4.79
c-0.49,2.27-1.03,5.27-1.03,5.27l4.36,1.23c1.17,0.32,2.96,1.06,3.53,0.1c0.4-0.68,0.65-0.53,0.27,0.2
c-0.13,0.26-0.93,3.35-1.05,3.97c-0.24,1.15-0.4,0.42-0.34,0.15c0.06-0.27,0.27-0.74-0.74-1.14c-1.01-0.4-4.42-1.55-7.43-2.4
c-2.52-0.9-5.14-1.51-7.8-1.84c-0.68,0.06-0.86,0.21-1.02,0.44c-0.23,0.32-0.63,0.42-0.31-0.11c0.53-1.34,0.93-2.72,1.2-4.13
c0.2-1.34,0.4-0.4,0.38-0.15c-0.04,0.44,0.21,0.86,0.62,1.03c2.25,0.91,4.56,1.66,6.91,2.25c0,0,0.7-2.75,1.16-4.87
c0.55-2.5,0.92-4.97,0.92-4.97s-1.38-0.22-2.36-0.4c-1.68-0.31-3.38-0.5-5.09-0.55c-0.35,0.1-0.64,0.35-0.78,0.69
c-0.07,0.23-0.52,0.66-0.27-0.16c0.3-1.35,0.5-2.71,0.59-4.09c0.06-1.15,0.36-0.65,0.37-0.32c0.05,0.33,0.24,0.62,0.52,0.8
c2.54,0.75,5.14,1.25,7.78,1.49c2.75,0.55,5.54,0.85,8.33,0.9c0.64-0.22,0.6-0.46,0.72-0.73
C424.41,371.88,424.76,371.47,424.58,372.31z"/>
<path id="Path_1198" class="st0" d="M406.64,417.68c-1.1,1.09-2.11,2.27-3.02,3.51c-0.47,0.64-0.4,0.13-0.28-0.12
c0.23-0.46,0.09-0.61-0.09-1.01c-1.66-1.83-3.5-3.48-5.48-4.95c-1.87-1.83-3.98-3.4-6.28-4.67c-0.4,0-0.73-0.05-1.54,0.69
c-1.12,1.04-2.12,2.21-2.98,3.47c-0.3,0.57-0.43,1.22-0.37,1.87c0,0.44-0.13,0.4-0.28,0c-0.26-0.8-0.46-1.62-0.61-2.45
c-0.03-0.25,0.07-0.49,0.26-0.65c0.45-0.49,4.28-4.83,4.84-5.45c0.64-0.69,1.23-1.42,1.78-2.19c0.36-0.62,0.48-0.18,0.37,0.14
c-0.23,0.38-0.29,0.84-0.18,1.27c1.76,2.1,3.81,3.94,6.08,5.48c2.02,1.96,4.29,3.65,6.74,5.02c0.33,0.05,0.66-0.06,0.9-0.28
C406.68,417.2,407.16,417.32,406.64,417.68z"/>
<path id="Path_1199" class="st0" d="M383.18,417.49c0.2,0.5,2.15,3.85,4.18,7.26c2.11,3.52,4.4,7.19,4.7,7.75
c0.62,1.08,0.64,1.34-0.43,0.8c-1.55-0.77-7.76-3.9-8.79-4.4c-1.03-0.5-6.11-3.08-6.11-3.08s1.1,8.29,1.2,8.75
c0.1,0.47,0.98,7.37,1.03,7.72c0.05,0.35,0.05,0.9-0.58,0.31c-0.62-0.59-10.43-13.1-10.92-13.67c-0.35-0.45-0.87-0.75-1.43-0.84
c-0.26-0.05-0.52-0.04-0.77,0.04c-0.25,0.09-0.44-0.14-0.08-0.28c0.44-0.17,3.94-1.97,4.28-2.19c0.22-0.13,0.52,0.04,0.18,0.25
c-0.44,0.27-0.5,0.46-0.4,0.86c0.22,0.95,7.32,9.77,7.32,9.77s-1.2-7.92-1.41-8.93c-0.21-1.01-0.9-5.68-0.9-5.68s0-0.3,0.3-0.14
c0.3,0.16,7.24,3.7,7.99,4.06s5.19,2.56,5.19,2.56s-3.9-6.33-5.51-8.85c-0.23-0.44-0.6-0.78-1.05-0.97
c-0.55-0.11-0.72,0.12-1.04,0.26c-0.16,0.07-0.4-0.13,0.07-0.4c0.57-0.35,1.11-0.74,1.62-1.17c0.4-0.31,1.4-1.27,1.56-1.43
c0.16-0.16,0.4,0.04,0.23,0.21C383.12,416.36,382.93,416.98,383.18,417.49z"/>
<path id="Path_1200" class="st0" d="M340.34,441.57c-0.04,0.33,0.06,4.16,0.1,4.64c0,0.22-0.18,0.16-0.31-0.33
c-0.12-0.38-0.42-0.67-0.8-0.78c-1.42-0.18-2.86-0.21-4.3-0.08c-0.09,1.66,0.04,3.32,0.38,4.95c0.44,0.48,0.51,0.43,0.7,0.47
c0.2,0.04,0.67,0.29,0.36,0.26c-0.6-0.06-3.94-0.06-4.69,0.02c-0.55,0.06-0.27-0.2,0.19-0.3c0.46-0.1,0.7-0.36,0.87-0.94
c0.13-1.49,0.16-2.99,0.09-4.49c-1.53-0.05-3.07,0.03-4.59,0.24c-0.33,0.19-0.54,0.54-0.55,0.92c-0.04,0.32-0.38,0.6-0.33,0.17
c0.1-1.38,0.11-2.76,0.04-4.14c-0.08-0.55,0.05-0.47,0.23-0.14c0.22,0.4,0.15,0.63,0.62,0.82c1.52,0.15,3.05,0.21,4.58,0.16
c0,0,0.03-3.77-0.08-4.35c-0.07-0.5-0.46-0.89-0.97-0.96c-0.27-0.02-0.52-0.27-0.16-0.23c1.49,0.11,2.99,0.11,4.48,0
c0.48-0.05,0.2,0.21-0.16,0.3c-0.47,0.09-0.85,0.45-0.95,0.92c-0.15,0.58-0.13,4.29-0.13,4.29s3.51,0.04,4.15-0.09
c0.48-0.08,0.86-0.46,0.94-0.94C340.13,441.64,340.38,441.25,340.34,441.57z"/>
<path id="Path_1201" class="st0" d="M308.51,450.59c-1.62-0.79-3.33-1.4-5.09-1.82c-0.56-0.09-0.04-0.24,0.31-0.21
c0.46,0.04,0.85,0.1,1.2-0.25c1.96-3.73,3.55-7.63,4.77-11.66c0.91-2.44-0.32-5.15-2.76-6.06c-0.18-0.07-0.36-0.12-0.55-0.17
c-4-1.43-5.59,0.75-7.77,4.84c-1.58,2.86-2.95,5.84-4.11,8.89c-0.03,0.33,0.12,0.65,0.4,0.83c0.36,0.29,0.56,0.76,0.22,0.49
c-1.39-0.86-2.86-1.58-4.4-2.13c-0.34-0.11-0.07-0.3,0.36-0.21c0.36,0.08,0.52,0,0.87-0.25c2.12-3.33,4.05-6.76,5.81-10.3
c1.05-1.94,2.89-3.34,5.04-3.82c3.29-0.33,6.5,1.08,8.5,3.71c1.19,1.86,1.49,4.16,0.8,6.27c-0.4,1.65-4,9.69-4,10.79
c-0.02,0.31,0.13,0.6,0.4,0.76C308.73,450.45,308.86,450.8,308.51,450.59z"/>
<path id="Path_1202" class="st0" d="M283.31,439.86c0,0-0.75-8.14-0.98-10.95c-0.26-3.27-1.08-9.59-1.08-9.59
s-6.39,6.92-7.22,7.76c-0.83,0.84-1.6,1.67-1.6,2.24c0,0.33,0.13,0.65,0.36,0.9c0.08,0.16,0.22,0.72,0.06,0.52
c-0.55-0.63-1.14-1.22-1.75-1.78c-0.54-0.49-1.1-0.95-1.69-1.37c-0.44-0.29,0.1-0.24,0.34-0.09c0.25,0.19,0.57,0.26,0.88,0.19
c2.46-2.14,4.77-4.45,6.91-6.91c1.71-1.78,5.54-5.99,5.94-6.54c0.28-0.4,0.28-0.22,0.31,0.2c0.04,0.52,0.71,8.89,0.85,9.86
c0.13,0.9,0.56,6.59,0.62,7.23s0.25,2.77,0.25,2.77s2.94-4.22,3.92-5.75c1.19-1.53,2.2-3.2,3.02-4.96
c0.01-0.5-0.24-0.96-0.66-1.23c-0.13-0.14-0.24-0.48,0.06-0.22c1.25,1.05,2.61,1.95,4.05,2.71c0.62,0.29-0.31,0.11-0.47,0.04
c-0.39-0.12-0.81-0.12-1.2,0c-1.87,2.19-3.57,4.52-5.08,6.97c-0.76,1.11-2.98,4.22-3.5,5.07c-0.52,0.84-1.71,2.59-1.95,3.03
C283.47,440.36,283.37,440.33,283.31,439.86z"/>
<path id="Path_1203" class="st0" d="M275.07,405.78c0.44,0.76,0.94,1.48,1.49,2.17c0.57,0.71,1.18,1.37,1.84,2
c0.35,0.33-0.14,0.21-0.46-0.03c-0.32-0.24-0.69-0.35-1.71,0.2c-1.2,0.64-11.68,9.47-12.08,9.99c-0.49,0.6-0.45,0.9-0.24,1.36
c0.16,0.36,0.05,0.67-0.09,0.44c-0.91-1.41-1.95-2.73-3.11-3.94c-0.46-0.34,0.02-0.26,0.32-0.08c0.3,0.17,0.4,0.17,0.76,0
c4.64-3.28,9.06-6.87,13.22-10.75c0.12-0.34,0.08-0.72-0.1-1.03C274.9,405.84,274.84,405.38,275.07,405.78z"/>
<path id="Path_1204" class="st0" d="M265.98,334.45c-0.83,1.65-1.51,3.36-2.03,5.13c-0.1,0.46-0.33-0.02-0.26-0.36
c0.12-0.58,0.2-0.88-0.76-1.52c-4.82-2.22-9.79-4.07-14.88-5.56c-0.42-0.04-0.82,0.18-1.01,0.55c-0.2,0.26-0.62,0.37-0.36,0
c0.79-1.65,1.47-3.35,2.03-5.09c0.09-0.56,0.31,0,0.25,0.32c-0.14,0.37-0.05,0.78,0.23,1.05c5,2.47,10.22,4.48,15.58,5.99
c0.68-0.11,0.61-0.34,0.84-0.55C265.83,334.2,266.3,333.97,265.98,334.45z"/>
<path id="Path_1205" class="st0" d="M261.32,402.92l3.97-5.3c0,0-6.96,0.38-8.45,0.5c-1.49,0.12-5.82,0.31-6.31,1.01
c-0.32,0.47,0,0.99,0,1.15c0.06,0.32,0,0.82-0.16,0.44c-0.08-0.24-0.62-1.65-1.05-2.63c-0.4-0.88-0.9-1.95-1.03-2.15
c-0.22-0.32,0.15-0.24,0.36,0c0.23,0.29,0.57,0.49,0.94,0.56c2.9,0.11,5.81,0.07,8.7-0.12c1.25-0.05,10.45-0.65,11.22-0.58
c0.4,0.04,0.78-0.16,0.28,0.6c-0.28,0.43-5.77,7.62-6.19,8.2c-2.04,2.56-3.92,5.24-5.63,8.02c-0.06,0.36-0.04,0.72,0.06,1.07
c0.06,0.28,0.02,1.05-0.08,0.8c-0.9-1.74-1.93-3.4-3.09-4.99c-0.4-0.51,0.13-0.16,0.44-0.05c0.31,0.12,0.5,0.04,0.85-0.2
C258.04,407.27,259.76,405.15,261.32,402.92z"/>
<path id="Path_1206" class="st0" d="M261.9,376.76c0.26,0.75,1.07,4.05,1.48,5.49c0.14,0.51,1.05,3.87,1.33,4.43
c0.13,0.27-0.47-0.16-0.59-0.44c-0.04-0.22-0.16-0.41-0.35-0.54c-0.19-0.11-0.4-0.17-0.62-0.18c-2.73,0.54-5.42,1.29-8.04,2.22
c-2.63,0.67-5.2,1.59-7.66,2.75c-0.32,0.28-0.43,0.73-0.29,1.13c0.04,0.26-0.16,1.2-0.26,0.61c-0.15-1.04-0.37-2.07-0.64-3.08
c-0.4-1.38-2-7.08-2.07-7.32c-0.13-0.32-0.15-0.44,0.09-0.53c0.72-0.24,1.43-0.52,2.13-0.84c0.5-0.3,0.31,0.13,0.03,0.42
c-0.28,0.29-0.4,0.43-0.36,1.32c0.34,1.68,0.78,3.34,1.33,4.96c0,0,2.35-0.62,3.12-0.9s3.32-1.03,3.32-1.03
c-0.4-1.61-0.9-3.19-1.51-4.74c-0.37-0.49-0.62-0.4-1-0.36c-0.28,0.02-0.76-0.18-0.4-0.26c1.38-0.3,2.72-0.72,4.02-1.25
c0.45-0.22,0,0.34-0.21,0.46c-0.38,0.2-0.6,0.61-0.56,1.04c0.08,0.86,0.28,1.71,0.58,2.53c0.17,0.48,0.65,2.12,0.65,2.12
s4.25-1.23,4.96-1.56c0.71-0.34,1.26-0.34,1.11-1.75c-0.24-1.68-0.75-3.31-1.51-4.83c-0.26-0.41-0.6-0.77-0.99-1.05
c-0.16-0.13-0.72-0.6-0.4-0.49c0.98,0.31,1.94,0.68,2.88,1.1C261.66,376.3,261.82,376.51,261.9,376.76z"/>
<path id="Path_1207" class="st0" d="M282.78,309.83c-0.51,0.54-4.24,4.58-5.29,5.76c-0.35,0.4-1.2,1.35-1.43,1.64
c-0.19,0.22-0.28-0.22-0.12-0.49c0.25-0.29,0.29-0.7,0.12-1.04c-1.98-2.22-4.16-4.24-6.51-6.06c-1.86-1.81-3.89-3.44-6.07-4.86
c-0.4-0.09-0.81-0.03-1.16,0.19c-0.24,0.12-0.8,0.36-0.4-0.03c0.4-0.39,1.02-1.07,1.91-2.04c0.97-1.05,4.96-5.19,5.12-5.41
s0.32-0.28,0.49-0.1c0.5,0.53,1.03,1.03,1.6,1.49c0.6,0.36,0.06,0.32-0.25,0.25c-0.32-0.07-0.43-0.17-1.08,0.21
c-1.27,1.06-2.43,2.24-3.47,3.52l4.77,4.4c0,0,3.32-3.09,3.38-3.72c0.05-0.27,0.03-0.54-0.07-0.8c-0.08-0.25-0.05-0.69,0.16-0.4
c0.77,0.95,1.65,1.82,2.61,2.57c0.4,0.3-0.07,0.3-0.3,0.2c-0.35-0.15-0.48-0.4-1.13-0.08c-1.19,1.06-2.29,2.21-3.31,3.44
c0,0,3.4,2.92,4.04,3.38c0.83,0.61,1.43,0.76,2.26,0c1.23-1.06,2.3-2.28,3.2-3.64c0.2-0.46,0.28-0.97,0.23-1.47
c0.02-0.3,0.2-0.66,0.25-0.42c0.05,0.24,0.75,2.4,0.8,2.75C283.17,309.42,283.09,309.5,282.78,309.83z"/>
<path id="Path_1208" class="st0" d="M260.58,367.65c0.03,0.4,0.13,2.31,0.26,3.16c0.07,0.48-0.22-0.12-0.29-0.36
c-0.04-0.3-0.23-0.56-0.5-0.69c-2.67-0.18-5.35-0.07-7.99,0.34c-2.98,0.1-5.94,0.5-8.84,1.2c-0.31,0.32-0.46,0.76-0.4,1.2
c0,0.26-0.46,1.11-0.45,0.47c0-0.4,0-2.1-0.1-2.7c-0.29-2.05-0.39-4.12-0.3-6.18c0.46-2.23,2.37-3.88,4.65-4
c1.97-0.01,3.76,1.12,4.61,2.89c0.33-0.44,0.74-0.82,1.2-1.12c2.7-1.58,5.24-3.4,7.59-5.46c0.19-0.38,0.24-0.81,0.15-1.23
c-0.06-0.72,0.1-1.23,0.45,0c0.34,1.36,0.15,2.8-0.53,4.03c-1.57,1.59-3.34,2.96-5.27,4.08c-0.8,0.55-2.67,1.6-2.67,2.7
c-0.01,0.54,0.04,1.07,0.15,1.6c0,0,2.74-0.28,3.48-0.36c1.37-0.03,2.73-0.26,4.03-0.7c0.22-0.2,0.35-0.5,0.34-0.8
c0.03-0.21,0.47-1.13,0.44,0.06C260.52,366.18,260.54,367.25,260.58,367.65z M249.41,363.17c-1.94-1.24-4.51-0.73-5.83,1.16
c-0.48,1.23-0.61,2.56-0.37,3.86l3.94-0.26c0.62-0.04,4.06-0.28,4.06-0.28s0.35-3.2-1.8-4.49V363.17z"/>
<path id="Path_1209" class="st0" d="M328.95,307.11c-2.39-2.85-3.7-6.45-3.71-10.16c0.09-2.34,0.98-4.59,2.52-6.36l0.92-1.13
c2.28-2.26,3.59-5.33,3.63-8.55c0.12-2.01-0.11-4.03-0.67-5.96c-0.82,6.44-4.1,12.31-9.16,16.38c-2.28,1.98-6.09,5.28-6.09,9.41
c0.26,4.61,2.07,8.99,5.12,12.45c-0.91-2.36-1.38-4.86-1.38-7.39c-0.02-1.72,0.19-3.43,0.62-5.09l0.07-0.23l0.12,0.21
C322.71,303.76,325.57,306.05,328.95,307.11z"/>
<path id="Path_1210" class="st0" d="M330.13,289.41c2-0.77,9.17-3.91,9.17-8.94c-0.28-3.2-1.64-6.22-3.87-8.54
C337.54,278.29,335.42,285.29,330.13,289.41z"/>
<path id="Path_1211" class="st0" d="M357.06,279.77c-2.69,2.53-5.86,4.47-9.33,5.72l-0.12-0.17c2.95-3.43,4.81-7.66,5.35-12.15
c-5.67,8.93-9.13,10.47-13.13,12.23l-1.75,0.8c-4.79,2.27-10.29,5.51-10.29,10.6c-0.38,3.7,0.72,7.4,3.06,10.29
c-0.43-1.47-0.65-2.98-0.67-4.51c-0.03-1.03,0.1-2.07,0.4-3.06l0.15-0.4l0.06,0.4c1.05,3.22,3.15,6,5.97,7.89
c-0.08-0.8-0.13-1.61-0.12-2.42c0-3.75,0.87-10.39,6.69-12.88C353.83,287.62,356.45,281.82,357.06,279.77z"/>
<path id="Path_1212" class="st0" d="M348.36,300.57c-0.41-3.63,0.3-7.3,2.03-10.52c-7.5,2.91-12.46,10.11-12.51,18.15
c0,0.45,0,0.8,0.04,1.05c1.28-3.54,3.47-6.69,6.35-9.12l0.07-0.06l0.07,0.04c0,0,0.04,0.03,0.04,0.13
c-0.44,2.78-1.02,5.54-1.75,8.26C343.72,307.86,348.36,304.64,348.36,300.57z"/>
<path id="Path_1213" class="st0" d="M358.27,299.89c0-1.47-0.27-2.94-0.8-4.32c-0.48,1.66-1.42,3.15-2.71,4.3l0,0l-0.15-0.15
c0.92-1.44,1.34-3.15,1.17-4.86c-0.01-1.21-0.12-2.41-0.34-3.6c-0.09-0.51-0.14-1.03-0.14-1.55c0.06-2.13,0.69-4.2,1.83-5.99
c-2.9,2.21-4.97,5.33-5.88,8.86c-0.62,2.18-1.11,4.39-1.48,6.62c-0.11,0.79-0.28,1.58-0.51,2.35c-0.8,2.81-1.89,5.54-3.25,8.13
c2.09-1.32,3.75-3.21,4.79-5.45l0.15-0.3l0.06,0.33c0.09,0.68,0.13,1.36,0.12,2.05c0.02,1.94-0.36,3.87-1.11,5.66
C351.51,310.78,358.27,305.09,358.27,299.89z"/>
<path id="Path_1214" class="st0" d="M370.27,288.65c-2.3-1.69-5.24-2.24-7.99-1.49l-0.34,0.11l0.22-0.28
c1.45-1.76,3.21-3.24,5.19-4.36c-3.06-0.62-6.22,0.44-8.29,2.76c-1.27,2.26-1.88,4.83-1.77,7.42c0,0.54,0,0.95,0.04,1.23
c2.32-4.03,2.59-4.02,2.67-4l0.1,0.04v0.08c0.09,0.51,0.13,1.03,0.12,1.55c-0.05,1.76-0.22,3.51-0.52,5.24
C363.83,289.06,368.9,288.61,370.27,288.65z"/>
<path id="Path_1215" class="st0" d="M336.4,428.59l0.43-19c0.29,0.41,0.56,0.84,0.8,1.29c0.33,0.72,0.6,1.48,0.8,2.25
c0.41,0.44,0.69,0.98,0.8,1.57c0.14,0.93,0.58,4.06,0.74,4.79c0.15,0.74,0.14,1.32,0.58,1.32c2.54,0,3.96-4.2,3.42-5.53
c-0.52-1.06-1.18-2.05-1.96-2.93c0,0-1.37-4.35-1.6-4.89c-0.23-0.54-1.22-1.03-1.51-1.22c-0.15-0.1-1.04-0.98-1.9-1.84l0.12-5.33
c-0.28,0.02-0.56,0.02-0.84,0c-2.28,0.03-4.48-0.87-6.09-2.49l0.93,31.99L336.4,428.59z M342.8,418.2
c-1.12,1.56-1.27,1.6-1.66,1.6s-0.4-1.71-0.14-2.59c0.26-0.88,0.88-1.81,1.71-1.76C343.39,415.51,343.93,416.64,342.8,418.2
L342.8,418.2z"/>
<path id="Path_1216" class="st0" d="M378.59,356.33c2.06,0.65,4.2,1.04,6.35,1.17c3.63-0.51,6.98-2.21,9.53-4.84
c1.31-0.91,2.68-1.72,4.1-2.44c0,0-2.64-0.05-5.96-0.34c-3.09-0.25-6.2,0.11-9.14,1.07c-1.67,0.61-3.19,1.58-4.45,2.83
c-0.5,0.57-1.14,0.99-1.86,1.22c-0.98,0.24-1.81,0.44-1.81,0.44c0.23-0.86,0.69-1.63,1.32-2.25c1.72-1.18,3.31-2.54,4.74-4.06
c2.29-2.74,4.23-5.76,5.77-8.98c0.24-0.93,2.25-5.43,2.25-5.43c-1.64,1.17-3.36,2.24-5.13,3.2c-2.82,1.49-5.22,3.65-6.99,6.31
c-1.2,2.26-1.84,4.78-1.86,7.34c-0.14,2.42-1.15,4.71-2.83,6.45c-1.57,1.7-3.27,3.29-5.08,4.74c-0.89,0.68-1.9,1.19-2.98,1.51
c-0.67,0.14-1.37,0.18-2.05,0.1c0,0,1.03-1.32,1.66-1.51c1.74-0.59,3.31-1.58,4.59-2.88c2.04-2.53,3.45-5.51,4.1-8.7
c0.2-0.93,1.07-3.76,1.07-3.76c-1.28,1.22-2.67,2.31-4.16,3.28c-2.62,1.74-4.84,4.02-6.5,6.7c-0.96,1.56-1.26,3.45-0.83,5.23
c0,0.4-0.14,0.64-0.8,1.27c-0.62,0.49-1.36,0.82-2.15,0.93c-1.63,0.6-3.35,0.94-5.09,1.03c-2.03-0.2-4.03-0.57-5.99-1.12
c-0.68-0.05-1.76,0.59-2.2,0.64c-0.41-0.02-0.82-0.07-1.22-0.14c-0.44,0-1.81,0.05-1.81,0.05c0.26-0.72,0.4-1.48,0.44-2.25
c0.05-0.34,0.15-0.83,0.64-0.93c0.83-0.37,1.63-0.79,2.4-1.27c0.44-0.22,0.93-0.32,1.42-0.29c1.89-0.04,3.73-0.55,5.38-1.47
c2.79-1.83,5.13-4.27,6.84-7.14c1.17-1.2,2.45-2.3,3.81-3.28c0,0-2.49,0.68-5.33,1.51c-3.03,0.89-5.88,2.28-8.45,4.11
c-1.11,1.01-2.05,2.18-2.8,3.47c-0.4,0.86-0.9,1.68-1.47,2.44c-0.44,0.34-1.86,1.07-1.86,0.88c0.23-0.63,0.52-1.24,0.88-1.81
c0.34-0.59,1.42-2.3,2.1-3.32c0.53-0.83,0.96-1.71,1.27-2.64c0.29-0.91,0.72-1.76,1.27-2.54c1.08-1.59,2.87-2.56,4.79-2.59
c2.02,0.02,4.05-0.08,6.06-0.3c2.63-0.42,5.13-1.43,7.33-2.93c0.8-0.44,4.25-2.98,4.25-2.98s-3.37,0.59-4.45,0.73
c-2.56,0.11-5.1,0.38-7.63,0.8c-2.34,0.75-4.54,1.89-6.5,3.37c-1.44,0.99-2.83,2.06-4.15,3.2c0-0.62,0.05-1.24,0.15-1.86
c0.11-0.82,0.6-1.55,1.32-1.96c2.01-1.1,3.88-2.44,5.57-4c1.77-1.95,3.25-4.13,4.4-6.5c0.3-0.88,2.05-4.4,2.05-4.4
c-1.18,1-2.49,1.82-3.91,2.45c-2.89,1.41-5.46,3.4-7.53,5.87c-1.14,1.39-1.82,3.1-1.95,4.89c-0.03,0.8-0.16,1.58-0.4,2.35
c-0.39,0.77-0.59,1.63-0.59,2.49c-0.05,0.96-0.26,1.91-0.64,2.8c-0.15,0.61-0.36,1.2-0.64,1.76c-0.26,0.34-0.48,0.72-0.64,1.12
c-0.68,1.15-1.45,2.24-2.3,3.28c0,0-0.64-0.8,0.34-2.88c1.12-2.23,1.8-4.65,2-7.14c-0.1-2.03-0.48-4.04-1.13-5.96
c-0.44-1.17-1.91-5.33-1.91-5.33c-0.13,1.32-0.41,2.61-0.83,3.86c-1.16,2.76-1.92,5.67-2.25,8.65c0.1,2.41,0.82,4.75,2.11,6.79
c0.62,0.75,0.89,1.73,0.73,2.69c-0.11,0.86-0.47,1.68-1.03,2.35c-0.36,0.72-0.6,1.5-0.68,2.3c-0.43,2.01-1.67,3.76-3.42,4.84
c-0.15,0.12-0.34,0.26-0.55,0.4l0.69-26.82c-0.01-6.13,1.5-12.17,4.4-17.58c0.47-0.68,0.72-1.01,0.72-1.01
c2.17-2.98,5.52-4.9,9.19-5.27v-2.19c0,0-16.85-0.03-20.13-0.03h-0.06c-3.26,0-18.44,0.03-18.44,0.03v2.19
c3.67,0.37,7.02,2.29,9.19,5.27c2.88,4,4.66,6.37,5.17,19.07l0.93,30.65c-1.35,0.86-2.27,2.26-2.53,3.85
c-0.4-0.59-0.49-1.33-0.24-2c0.84-1.83,1.13-3.87,0.83-5.87c-0.94-2.95-2.41-5.7-4.35-8.12c-0.93-1.07-2.64-3.71-2.64-3.71
s0.05,2.45-0.1,4.4c-0.53,2.69-0.41,5.47,0.34,8.11c0.77,2.19,2.21,4.09,4.1,5.43c0.61,0.49,1.01,1.19,1.12,1.96
c0.15,0.66,0.11,1.35-0.1,2c-0.02,0.72-0.11,1.44-0.24,2.15c-0.2,0.72-0.8,1.12-0.88,1.76c-0.17,1.01-0.41,2.01-0.73,2.98
c-0.15,0.54-0.49,0.64-0.93,1.37c-0.25,0.36-0.37,0.79-0.34,1.22c-0.94-0.76-1.8-1.63-2.54-2.59c-1.08-2.96-3.64-5.14-6.74-5.72
c-2.1-0.34-4.24-0.38-6.35-0.14c-1.78-0.04-3.56-0.25-5.31-0.63c1.53,0.49,2.89,1.43,3.89,2.69c1.89,2.1,4.2,3.77,6.79,4.89
c1.68,0.5,3.47,0.48,5.13-0.05c0.73-0.19,1.27-0.49,1.91-0.24c0.54,0.31,1,0.72,1.37,1.22c0,0-1.13,0.64-1.96,1.03
c-2.86,1-5.32,2.9-6.99,5.43c-0.88,2.14-1.52,4.37-1.91,6.65c-0.28,1.27-0.62,2.53-1.03,3.76c0,0,1.66-1.42,3.03-2.35
c2.13-1.27,4.02-2.9,5.57-4.84c1.55-2.38,2.44-5.13,2.59-7.97c0-0.57,0.42-1.05,0.98-1.13c0.58,0.18,0.99,0.71,1.03,1.32
c0.01,0.7,0.23,1.39,0.64,1.96c0.29,0.44,0.98,1.03,1.57,1.86c0.55,1.13,1.02,2.29,1.42,3.47c0.4,0.8,0.59,0.68,1.17,1.76
c0.59,1.07,0.49,1.66,0.98,2.49c0.49,0.83,0.73,0.74,0.73,0.74l-0.29-5.19c-0.15-0.09-0.29-0.2-0.4-0.34
c-0.19-0.28-0.35-0.57-0.49-0.88c0,0,0.88-0.59,1.47-0.88c0.42-0.25,0.94-0.21,1.32,0.1c1.78,1.42,3.94,2.28,6.21,2.49
c3.65-0.72,7.15-2.04,10.36-3.91c1.32-0.56,2.68-1.04,4.06-1.42c0,0-2.93-0.29-4.5-0.64c-2.37-0.53-4.77-0.94-7.19-1.22
c-0.67,0.01-1.34,0.09-2,0.24l0.13-1.57c0,0-1.29,1.09-1.73,1.43c-1.01,0.66-2.09,1.2-3.23,1.6c-0.76,0.29-1.55,0.52-2.35,0.68
c0,0-0.2-0.2-0.34,0.54s-0.24,0.64-1.07,0.83c-0.54,0.13-1.06,0.31-1.56,0.54c0,0,0.68-1.76,0.8-2.05
c0.12-0.29,0.24-0.58,1.27-0.8c2.61-0.53,5.03-1.76,6.99-3.57c2.16-2.06,3.87-4.54,5.03-7.28c0.93-1.6,2.1-3.05,3.47-4.3
c0,0-2.25,0.88-6.5,2.35c-0.2,0.07-0.4,0.14-0.59,0.22l0.15-5.61l0,0v-0.4l-0.29,0.14c-0.64,0.29-2.49,0.83-3.22,1.17
c-0.73,0.34-1.32,1.12-1.91,1.42c-1.03,0.38-1.97,0.96-2.76,1.71l0.05,0.92l0,0l0.19,5.99c-1.1,1.43-1.77,3.15-1.92,4.94
c-0.2,1.12-0.34,1.66-0.49,2.25c-0.21,0.66-0.45,1.32-0.73,1.96c-0.46-0.89-1.02-1.73-1.66-2.49c-0.57-0.62-0.74-1.51-0.44-2.3
c0.23-0.87,0.54-1.73,0.93-2.54c0.44-1.12,1.03-3.28,1.37-3.96c0.34-0.68,0.2,0.64,0.93-3.37c0.43-2.45,2.18-4.46,4.55-5.23
c0.59-0.3,1.17-1.07,1.91-1.42c0.73-0.34,2.59-0.88,3.22-1.17c1.39-0.53,2.64-1.37,3.67-2.45c0.54-0.84,1.28-1.52,2.15-2
c1.05-0.36,2.16-0.53,3.27-0.49c1.6-0.1,1.95,1.17,2.3,1.6c0.29,0.43,0.43,0.95,0.4,1.47c-0.15,2.49,0.93,4.9,2.88,6.45
c2.56,1.91,5.58,3.11,8.75,3.47c1.47,0.4,5.13,1.91,5.13,1.91s-0.64-0.24-3.13-4.11c-1.61-2.77-3.79-5.17-6.39-7.03
c-1.56-0.74-3.21-1.29-4.9-1.63c-0.58-0.1-1.13-0.33-1.6-0.68c-0.45-0.49-0.85-1.01-1.22-1.57c0.21-0.03,0.42-0.03,0.64,0
c1.54,0.67,3.21,0.99,4.89,0.93c1.97,0.01,3.92-0.46,5.67-1.37c0.63-0.25,1.34-0.18,1.91,0.2c0.64,0.4,1.42,0.93,2.98,1.91
c2.16,1.46,4.68,2.32,7.28,2.49c3.15-0.19,6.25-0.86,9.19-2c1.23-0.5,2.51-0.9,3.81-1.17c0,0-2.44-0.44-3.81-0.74
c-2.9-0.98-5.9-1.63-8.95-1.95c-2.01,0.01-3.97,0.59-5.67,1.66c-0.52,0.54-1.31,0.7-2,0.4c-0.71-0.14-1.28-0.64-1.51-1.32
c2.01-0.09,3.94-0.81,5.52-2.05c1.32-1.06,2.54-2.26,3.62-3.57c0.63-0.6,1.14-1.35,2.49-2.59
C375.76,356.13,377.27,355.8,378.59,356.33z M385.7,351.25c3.16-0.63,6.39-0.75,9.59-0.36c-0.8,0.2-5.67,2.3-9.09,3.2
c-2.43,0.6-4.9,0.99-7.4,1.17l-0.05-0.1C380.74,353.36,383.12,352.02,385.7,351.25z M378.35,344.26c2.09-2.87,4.93-5.1,8.22-6.43
c0,0-4.19,5.57-5.51,7.35c-1.33,1.79-4.75,6.84-4.75,6.84c-0.11-2.73,0.61-5.44,2.06-7.76H378.35z M364.66,356.77
c1.82-2.64,4.26-4.79,7.1-6.28c0,0-2.91,4.9-4.54,7.15c-1.63,2.25-4,5-4,5s-0.94-1.94,1.46-5.87H364.66z M351.85,354.32
c2.79-1.51,5.69-2.81,8.68-3.88c0,0-5,4.44-7.5,6.23c-1.75,1.21-3.6,2.29-5.51,3.22c0.82-2.27,2.35-4.22,4.36-5.57L351.85,354.32
z M359.97,344.36c2.9-0.57,5.84-0.89,8.79-0.97c0,0-5.77,2.04-8.12,2.8c-2.13,0.6-4.3,1.06-6.48,1.38
c1.76-1.37,3.73-2.46,5.83-3.21L359.97,344.36z M352.25,340.08c2.44-2.6,5.33-4.74,8.53-6.33c0,0-3.73,3.72-6.33,6.79
c-2.6,3.07-4.34,5.16-4.34,5.16l-0.05,0.1c0.34-2.04,1.09-3.99,2.21-5.73L352.25,340.08z M343.37,355.8
c-1.25-1.97-1.91-4.25-1.89-6.58c0.16-2.96,0.96-5.85,2.35-8.47c0,0-0.1,4.79,0.05,7.5c0.01,2.52-0.15,5.05-0.5,7.55
L343.37,355.8z M311.81,384.09c-2.02-0.85-3.95-1.89-5.77-3.12c2.84-0.4,5.73-0.26,8.53,0.4c2.17,0.87,3.84,2.67,4.54,4.9
c0,0-4.93-1.36-7.27-2.19L311.81,384.09z M310.8,402.47c0.31-2.93,1.17-5.78,2.54-8.39c1.3-1.77,3.15-3.06,5.27-3.65
L310.8,402.47z M338.47,391.52c2.83,0.11,5.64,0.5,8.39,1.15c-3.22,0.78-6.48,1.37-9.77,1.77c-2.06,0.14-4.13,0.11-6.19-0.08
c0,0,1.56-2.26,7.6-2.86L338.47,391.52z M332.12,383.39c2.86-1.96,6.01-3.46,9.34-4.44c0,0-5.61,4.79-7.86,6.79
c-1.81,1.7-3.51,3.51-5.1,5.41c-0.01-3.01,1.33-5.86,3.66-7.78L332.12,383.39z M325.9,374.99c-1.1-1.94-1.97-3.99-2.6-6.13
c-0.68-2.48-1.69-8.47-1.69-8.47c1.72,2.42,3.27,4.96,4.65,7.59c0.65,2.31,0.53,4.77-0.33,7L325.9,374.99z M350.15,368.87
c2.12,0.47,4.16,1.23,6.07,2.25c2.74,2.11,4.96,4.81,6.48,7.91c-2.44-1.33-4.8-2.81-7.04-4.44c-2.04-1.64-3.89-3.51-5.51-5.57
V368.87z M371.89,365.45c3.08,0.22,6.12,0.84,9.03,1.84c0,0-6.84,0.87-9.03,0.87c-1.97-0.07-3.93-0.26-5.87-0.56
c1.74-1.22,3.78-1.96,5.9-2.15L371.89,365.45z"/>
<path id="Path_1217" class="st0" d="M361.37,338.3c-0.91,0.5-1.44,1.51-1.33,2.55c-0.12,0.71,0.36,1.39,1.08,1.51
c0.23,0.04,0.48,0.01,0.7-0.07c1.01-0.3,1.66-1.27,1.55-2.32c0.04-0.89-0.65-1.66-1.54-1.7
C361.67,338.25,361.52,338.27,361.37,338.3z"/>
<path id="Path_1218" class="st0" d="M385.58,345.63c-0.91,0.5-1.44,1.5-1.33,2.54c-0.12,0.71,0.35,1.39,1.06,1.51
c0.24,0.04,0.48,0.02,0.71-0.07c1.01-0.3,1.67-1.28,1.55-2.33c0.04-0.9-0.65-1.65-1.55-1.7
C385.88,345.58,385.73,345.6,385.58,345.63z"/>
<path id="Path_1219" class="st0" d="M344.59,368.63c-0.91,0.51-1.43,1.51-1.33,2.55c-0.12,0.71,0.35,1.38,1.06,1.51
c0.24,0.04,0.48,0.02,0.71-0.08c1.01-0.3,1.66-1.27,1.55-2.32c0.04-0.9-0.65-1.66-1.55-1.71
C344.89,368.58,344.74,368.6,344.59,368.63z"/>
<path id="Path_1220" class="st0" d="M373.24,360.94c-0.33,0.64-0.08,1.43,0.56,1.77c0.21,0.11,0.45,0.16,0.68,0.14
c1.05,0.02,1.97-0.71,2.18-1.74c0.32-0.84-0.1-1.77-0.94-2.09c-0.15-0.06-0.3-0.09-0.45-0.1
C374.26,359.12,373.45,359.92,373.24,360.94z"/>
<path id="Path_1221" class="st0" d="M360.92,368.61c-0.91,0.51-1.43,1.51-1.33,2.55c-0.12,0.71,0.36,1.39,1.07,1.51
c0.24,0.04,0.48,0.02,0.7-0.07c1.01-0.3,1.66-1.28,1.55-2.33c0.04-0.9-0.65-1.66-1.55-1.7
C361.22,368.57,361.07,368.58,360.92,368.61z"/>
<path id="Path_1222" class="st0" d="M341.44,385.33c-0.91,0.5-1.44,1.5-1.33,2.54c-0.13,0.71,0.35,1.39,1.06,1.52
c0.24,0.04,0.48,0.02,0.71-0.07c1.01-0.3,1.66-1.28,1.55-2.33c0.04-0.9-0.65-1.65-1.55-1.7
C341.73,385.28,341.58,385.3,341.44,385.33z"/>
<path id="Path_1223" class="st0" d="M311.04,392.41c1.01-0.3,1.66-1.28,1.55-2.33c0.04-0.9-0.65-1.66-1.55-1.7
c-0.15-0.01-0.3,0.01-0.45,0.04c-0.91,0.51-1.44,1.51-1.33,2.55c-0.12,0.71,0.36,1.39,1.07,1.51
C310.57,392.52,310.82,392.5,311.04,392.41z"/>
<path id="Path_1224" class="st0" d="M320.23,271.21c1.67,2.3,2.61,5.05,2.72,7.89c0,1.07-0.12,2.8-0.12,2.8
c-0.17,1.67-0.48,3.32-0.93,4.93c1.49-2.11,2.64-4.43,3.43-6.89l0.06-0.27l0.13,0.24c0.54,1.04,0.89,2.17,1.02,3.34
c0.63-1.45,0.98-3,1.02-4.58C327.24,274.74,324.16,271.6,320.23,271.21z"/>
<path id="Path_1225" class="st0" d="M321.12,283.36L321.12,283.36c-0.07-1.23-0.21-2.46-0.43-3.68c-1.7,3.43-4.15,6.43-7.16,8.79
c-2.79,1.95-4.55,5.06-4.79,8.45c0.56,5.03,3.14,9.61,7.15,12.69c-1.24-3.34-1.89-6.88-1.93-10.44c0.12-1.87,1.04-3.59,2.53-4.73
l0.3-0.3c1.12-1.06,2.14-2.21,3.05-3.46l0,0c0.48-0.72,0.84-1.51,1.06-2.34c0.09-0.39,0.15-0.8,0.19-1.2l0,0
C321.18,285.88,321.2,284.62,321.12,283.36z"/>
<path id="Path_1226" class="st0" d="M290.99,341.24c-0.91,0-1.65,0.74-1.65,1.65c0,0.01,0,0.01,0,0.02
c0.01,0.41,0.18,0.79,0.47,1.08c-0.19-0.06-0.39-0.1-0.59-0.1c-1.15,0-2.07,0.94-2.07,2.08c0,1.14,0.93,2.06,2.07,2.07
c0.18-0.01,0.37-0.03,0.55-0.08c-0.46,1.51-1.87,2.53-3.44,2.51c-3.05,0-4.27-3.29-4.4-4.07c0.32,1.08,1.45,1.71,2.54,1.39
c1.08-0.32,1.71-1.45,1.39-2.54c-0.26-0.87-1.06-1.47-1.97-1.47c-0.21,0.01-0.41,0.05-0.61,0.12c0.84-0.75,0.92-2.04,0.17-2.89
c-0.75-0.84-2.04-0.92-2.89-0.17c-0.43,0.38-0.68,0.93-0.69,1.51c0.01,0.6,0.27,1.17,0.72,1.57c-0.21-0.08-0.44-0.13-0.67-0.14
c-1.13-0.01-2.06,0.9-2.07,2.04c-0.01,1.13,0.9,2.06,2.04,2.07c0.92,0.01,1.74-0.6,1.99-1.49c-0.12,0.94-1.84,4.07-4.4,4.07
c-1.59,0.06-3.02-0.97-3.46-2.51c0.18,0.05,0.36,0.07,0.55,0.08c1.15,0,2.07-0.94,2.07-2.08c0-1.14-0.93-2.06-2.07-2.07
c-0.2,0-0.4,0.03-0.59,0.1c0.29-0.28,0.46-0.67,0.47-1.08c0.01-0.91-0.73-1.66-1.64-1.66c-0.01,0-0.02,0-0.02,0
c-0.36-0.04-0.72,0.03-1.04,0.2c0.43,1.7,0.94,5.11,1.02,5.59l1.43,9.31h15.38c0,0,0-0.16,0.04-0.28l1.39-9.04
c0.31-2.11,0.68-4.34,1-5.59C291.7,341.27,291.34,341.2,290.99,341.24z"/>
<path id="Path_1227" class="st0" d="M320.05,341.24c-0.91,0-1.64,0.74-1.64,1.65c0,0,0,0.01,0,0.01
c0.01,0.41,0.18,0.79,0.47,1.08c-0.19-0.06-0.39-0.1-0.59-0.1c-1.15,0-2.07,0.94-2.07,2.08c0,1.14,0.93,2.06,2.07,2.07
c0.18-0.01,0.37-0.03,0.55-0.08c-0.46,1.51-1.87,2.53-3.44,2.51c-3.05,0-4.27-3.29-4.4-4.07c0.32,1.08,1.45,1.71,2.54,1.39
c1.08-0.32,1.71-1.45,1.39-2.54c-0.26-0.87-1.06-1.47-1.97-1.47c-0.21,0.01-0.41,0.05-0.61,0.12c0.84-0.75,0.92-2.04,0.17-2.89
c-0.75-0.84-2.04-0.92-2.89-0.17c-0.43,0.38-0.68,0.93-0.69,1.51c0.01,0.6,0.27,1.17,0.72,1.57c-0.21-0.08-0.44-0.13-0.67-0.14
c-1.13-0.01-2.06,0.9-2.07,2.04c-0.01,1.13,0.9,2.06,2.04,2.07c0.92,0.01,1.74-0.6,1.99-1.49c-0.12,0.94-1.84,4.07-4.4,4.07
c-1.59,0.06-3.02-0.97-3.46-2.51c0.18,0.05,0.36,0.07,0.55,0.08c1.15,0,2.07-0.94,2.07-2.08c0-1.14-0.93-2.06-2.07-2.07
c-0.2,0-0.4,0.03-0.59,0.1c0.29-0.28,0.46-0.67,0.47-1.08c0.01-0.91-0.73-1.66-1.64-1.66c-0.01,0-0.02,0-0.03,0
c-0.36-0.04-0.72,0.03-1.04,0.2c0.43,1.7,0.94,5.11,1.02,5.59l1.43,9.31h15.38c0,0,0-0.16,0.04-0.28l1.39-9.04
c0.31-2.11,0.72-4.47,1-5.59C320.77,341.27,320.41,341.2,320.05,341.24z"/>
<path id="Path_1228" class="st0" d="M306.66,359.85c-1,0-1.81,0.81-1.81,1.81c0,0.01,0,0.01,0,0.02c0.01,0.45,0.19,0.88,0.52,1.2
c-1.21-0.36-2.48,0.32-2.84,1.53c-0.36,1.21,0.32,2.48,1.53,2.84c0.21,0.06,0.44,0.1,0.66,0.1c0.2-0.01,0.41-0.04,0.6-0.09
c-0.51,1.66-2.06,2.79-3.8,2.76c-3.37,0-4.71-3.63-4.83-4.49c0.35,1.2,1.61,1.88,2.8,1.53c1.2-0.35,1.88-1.61,1.53-2.8
c-0.28-0.96-1.17-1.63-2.17-1.62c-0.23,0.01-0.45,0.05-0.67,0.13c0.93-0.83,1.02-2.25,0.19-3.19c-0.83-0.93-2.25-1.02-3.19-0.19
c-0.48,0.42-0.75,1.03-0.76,1.67c0.01,0.66,0.3,1.29,0.8,1.73c-0.23-0.09-0.48-0.14-0.73-0.15c-1.25-0.01-2.27,0.99-2.28,2.25
c-0.01,1.25,0.99,2.27,2.25,2.28c1.02,0.01,1.91-0.66,2.2-1.64c-0.13,1.03-2.03,4.49-4.85,4.49c-1.76,0.06-3.33-1.07-3.82-2.76
c0.2,0.05,0.4,0.08,0.6,0.09c1.26,0.05,2.33-0.93,2.38-2.19c0.05-1.26-0.93-2.33-2.19-2.38c-0.06,0-0.13,0-0.19,0
c-0.22,0-0.44,0.04-0.65,0.11c0.33-0.31,0.51-0.75,0.52-1.2c0.01-1-0.8-1.83-1.8-1.83c-0.01,0-0.02,0-0.03,0
c-0.39-0.05-0.79,0.03-1.14,0.22c0.48,1.88,1.03,5.63,1.12,6.15l1.57,10.27h16.96c0,0,0.02-0.17,0.04-0.3l1.53-9.99
c0.34-2.33,0.8-4.94,1.1-6.15C307.45,359.86,307.05,359.79,306.66,359.85z"/>
</g>
</g>
<g>
<path class="st0" d="M292.05,156.13h-5.16c-0.07,6.74-0.14,13.42-0.14,24.7c0,5.99,0.41,9.36,2.41,11.83
c1.93,2.41,5.43,3.44,10.39,3.44c4.82,0,8.53-1.17,10.73-4.06c2-2.62,2.06-6.74,2.13-13.28l0.14-22.63h-5.99v-4.34h16.65v4.34
h-5.3l-0.14,26.08c0,5.5-0.62,9.49-3.78,13.35c-2.75,3.37-7.84,5.23-14.65,5.23c-8.39,0-13-2.13-15.82-5.99
c-2.27-3.1-2.68-6.47-2.68-14.52c0-7.84,0.07-16.79,0.14-24.15h-5.57v-4.34h16.65V156.13z"/>
<path class="st0" d="M350.99,195.62h4.54c0-5.5,0.28-11.28,0.28-16.99c0-3.44-0.14-10.32-8.05-10.32
c-5.02,0-10.53,3.03-10.53,12.66l-0.14,14.65h5.16v4.33h-15.41v-4.33h4.54l0.28-26.83h-5.71v-4.33h11.35
c0,1.38-0.07,3.3-0.21,4.27l-0.55,3.58h0.14c1.51-5.37,7.22-8.67,12.59-8.67c9.77,0,12.18,6.6,12.18,13.35
c0,6.05-0.27,13.14-0.27,18.65h5.23v4.33h-15.41V195.62z"/>
<path class="st0" d="M376.65,168.79h-6.47v-4.33h12.11l-0.28,31.17h5.64v4.33h-16.92v-4.33h5.64L376.65,168.79z M378.79,146.5
c2.34,0,4.27,1.93,4.27,4.27c0,2.34-1.93,4.27-4.27,4.27c-2.34,0-4.27-1.93-4.27-4.27C374.52,148.42,376.45,146.5,378.79,146.5z"
/>
<path class="st0" d="M387.85,164.45h14.59v4.33h-4.27l8.19,22.77c0.34,0.96,0.55,2,0.76,3.03h0.14c0.21-1.03,0.41-2.06,0.76-3.03
l8.39-22.77h-4.61v-4.33h14.17v4.33h-3.92l-12.25,31.17h-5.64l-12.32-31.17h-3.99V164.45z"/>
<path class="st0" d="M433.97,182.68c0.21,7.29,3.37,13.42,13.48,13.42c3.85,0,7.7-1.17,10.66-2.68l-0.21,4.88
c-2.61,1.31-8.12,2.48-11.7,2.48c-11.21,0-18.16-6.12-18.16-18.51c0-10.46,5.64-18.64,16.44-18.64
c14.04,0,15.34,12.73,15.34,19.06H433.97z M453.92,178.35c0-5.57-3.51-10.39-9.43-10.39c-6.05,0-10.18,4.54-10.52,10.39H453.92z"
/>
<path class="st0" d="M470.71,168.79h-6.05v-4.33h11.7v1.93c0,2.06-0.41,6.26-0.76,9.15h0.07h0.14c1.38-5.23,3.23-11.7,12.04-11.7
c0.48,0,3.3,0.28,3.58,0.55l-0.55,5.09c-0.96-0.34-2.48-0.55-3.85-0.55c-7.29,0-10.53,7.16-10.66,13.42l-0.28,13.28h5.99v4.33
h-16.65v-4.33h5.02L470.71,168.79z"/>
<path class="st0" d="M519.81,174.29h-5.09l-0.28-5.02c-1.31-1.1-4.61-1.31-6.26-1.31c-3.58,0-8.19,1.17-8.19,5.44
c0,3.85,3.44,5.02,8.67,5.92c9.01,1.51,12.8,4.27,12.8,10.11c0,6.33-4.27,11.35-14.52,11.35c-3.85,0-9.97-0.96-12.18-2.13
l-0.27-9.08h5.09l0.27,5.3c2.13,1.03,5.71,1.58,8.05,1.58c3.72,0,8.19-1.58,8.19-5.92c0-3.51-3.65-4.75-7.5-5.57
c-9.49-2.06-14.24-3.72-14.24-10.66c0-5.71,3.79-10.66,13.97-10.66c2.96,0,8.46,0.96,11.21,2.13L519.81,174.29z"/>
<path class="st0" d="M532.86,168.79h-6.47v-4.33h12.11l-0.28,31.17h5.64v4.33h-16.92v-4.33h5.64L532.86,168.79z M534.99,146.5
c2.34,0,4.27,1.93,4.27,4.27c0,2.34-1.93,4.27-4.27,4.27c-2.34,0-4.27-1.93-4.27-4.27C530.72,148.42,532.65,146.5,534.99,146.5z"
/>
<path class="st0" d="M546.4,164.45h5.71l0.27-8.67l5.64-0.34l-0.27,9.01h11.15v4.33h-11.22c-0.14,5.92-0.27,11.9-0.27,17.82
c0,5.09,0,9.5,5.44,9.5c2.89,0,5.5-0.83,7.02-1.44l-0.21,4.47c-1.79,0.89-5.09,1.65-7.98,1.65c-9.91,0-9.91-7.09-9.91-11.63
c0-4.34,0.21-15.21,0.28-20.37h-5.64V164.45z"/>
<path class="st0" d="M571.76,164.45h15v4.33h-4.47l8.46,22.08c0.41,1.24,0.83,2.89,0.96,3.72h0.14c0.14-0.83,0.55-2.48,0.96-3.72
l7.91-22.08h-5.02v-4.33h14.17v4.33h-3.92l-10.87,29.93c-5.02,13.97-9.08,19.33-18.03,19.33c-1.72,0-3.16-0.27-4.2-0.62v-4.68
c0.83,0.28,2.61,0.62,3.99,0.62c4.06,0,8.26-2.48,10.94-8.94l1.17-2.89l-13-32.75h-4.2V164.45z"/>
<path class="st0" d="M276.94,127.75h5.66v6.56c2.21,1.31,6.42,1.79,9.46,1.79c6.9,0,11.46-3.18,11.46-8.98
c0-12.91-26.52-5.25-26.52-22.58c0-5.87,3.45-13.74,17.88-13.74c3.52,0,9.94,0.9,12.98,2.28v9.74h-5.38v-6.01
c-1.8-0.76-5.8-1.31-8.08-1.31c-5.18,0-11.05,2-11.05,8.36c0,12.29,26.52,4.63,26.52,22.44c0,10.63-9.12,14.5-18.44,14.5
c-7.94,0-11.81-1.86-14.5-2.42V127.75z"/>
<path class="st0" d="M314.23,104.34h5.73l0.28-8.7l5.66-0.34l-0.28,9.04h11.19v4.35h-11.25c-0.14,5.94-0.28,11.95-0.28,17.88
c0,5.11,0,9.53,5.46,9.53c2.9,0,5.52-0.83,7.04-1.45l-0.21,4.49c-1.79,0.9-5.11,1.66-8.01,1.66c-9.94,0-9.94-7.11-9.94-11.67
c0-4.35,0.21-15.26,0.28-20.44h-5.66V104.34z"/>
<path class="st0" d="M358.07,103.51c10.84,0,17.26,7.46,17.26,17.68c0,11.33-6.42,19.61-17.26,19.61
c-10.84,0-17.26-7.46-17.26-17.67C340.8,111.79,347.23,103.51,358.07,103.51z M358.07,136.1c7.6,0,11.33-6.21,11.33-13.95
c0-7.73-3.73-13.95-11.33-13.95c-7.59,0-11.32,6.21-11.32,13.95C346.74,129.89,350.47,136.1,358.07,136.1z"/>
<path class="st0" d="M409.78,139.07c-2.76,0.83-6.35,1.73-10.29,1.73c-14.09,0-18.58-9.32-18.58-18.64
c0-13.6,9.87-18.64,17.82-18.64c3.94,0,7.87,0.48,11.53,1.79v9.32h-5.11v-5.59c-1.52-0.55-4.35-0.83-5.94-0.83
c-10.29,0-12.36,8.15-12.36,13.95c0,1.93,0.28,13.95,13.53,13.95c3.52,0,6.35-0.97,9.67-2L409.78,139.07z"/>
<path class="st0" d="M420.15,91.22h-6.91v-4.35h12.57l-0.14,33.14l13.6-11.19v-0.14h-4.97v-4.35h15.47v4.35h-3.66l-14.78,12.02
l16.09,14.92h3.87v4.35h-15.47v-4.35h4.28v-0.14l-14.43-13.74l-0.14,13.88h4.7v4.35h-15.47v-4.35h5.11L420.15,91.22z"/>
<path class="st0" d="M460.4,91.22h-6.91v-4.35h12.57L466,105.58c0,1.59-0.41,5.11-0.69,6.63h0.14c1.52-5.39,7.25-8.7,12.64-8.7
c9.81,0,12.22,6.63,12.22,13.39c0,6.08-0.28,13.19-0.28,18.71h5.25v4.35h-15.47v-4.35h4.56c0-5.53,0.27-11.33,0.27-17.06
c0-3.45-0.14-10.36-8.08-10.36c-5.04,0-10.57,3.04-10.57,12.71l-0.21,14.71h5.25v4.35h-16.02v-4.35h5.11L460.4,91.22z"/>
<path class="st0" d="M516.3,103.51c10.84,0,17.26,7.46,17.26,17.68c0,11.33-6.42,19.61-17.26,19.61
c-10.84,0-17.26-7.46-17.26-17.67C499.04,111.79,505.46,103.51,516.3,103.51z M516.3,136.1c7.6,0,11.32-6.21,11.32-13.95
c0-7.73-3.73-13.95-11.32-13.95c-7.6,0-11.32,6.21-11.32,13.95C504.98,129.89,508.7,136.1,516.3,136.1z"/>
<path class="st0" d="M542.77,91.22h-6.91v-4.35h12.57l-0.28,48.75h5.66v4.35h-16.43v-4.35h5.11L542.77,91.22z"/>
<path class="st0" d="M558.84,135.62h4.56l0.27-26.93h-5.73v-4.35h11.39c0,1.38-0.07,3.32-0.21,4.28l-0.55,3.59h0.14
c1.52-5.39,7.25-8.7,12.64-8.7c6.49,0,9.87,3.73,10.77,8.35h0.14c0.69-1.52,2.9-8.35,12.08-8.35c9.8,0,12.22,6.63,12.22,13.39
c0,6.08-0.28,13.19-0.28,18.71h5.25v4.35h-15.47v-4.35h4.56c0.07-5.8,0.27-11.53,0.27-17.26c0-5.18-1.11-10.15-7.73-10.15
c-4.63,0-10.15,3.04-10.22,12.71l-0.14,14.71h5.11v4.35h-15.54v-4.35h4.55c0.07-5.8,0.28-11.53,0.28-17.26
c0-5.18-1.1-10.15-7.73-10.15c-4.63,0-10.15,3.04-10.22,12.71l-0.14,14.71h5.18v4.35h-15.47V135.62z"/>
</g>
</g>
</svg>

After

(image error) Size: 40 KiB

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>

After

(image error) Size: 4.0 KiB

@ -0,0 +1,33 @@
import createClient, { Middleware } from "openapi-fetch";
import type { paths } from "../lib/api";
const client = createClient<paths>({
baseUrl: import.meta.env.VITE_BACKEND_URL,
});
const includeCredentials: Middleware = {
onRequest({ request, options }) {
return new Request(request, { ...options, credentials: "include" });
},
};
const initiateAuthorizationOnUnauthorized: Middleware = {
onResponse({ response }) {
if (response.status === 401) {
const authorizationUrl = response.headers.get("X-Authorization-Url");
if (authorizationUrl) {
window.location.href = authorizationUrl;
}
}
return response;
},
};
client.use(includeCredentials);
client.use(initiateAuthorizationOnUnauthorized);
export function useBackend() {
return {
client,
};
}

39
frontend/src/hooks/fetch.d.ts vendored Normal file

@ -0,0 +1,39 @@
// Using a .js & .d.ts file to make the types work well at use sites
// and make TypeScript, IDE, and eslint happy.
//
// Important to use `useMemo` on any options object passed in
// since it is used as a dependency to re-fetch
import {
PathsWithMethod,
type RequiredKeysOf,
} from "openapi-typescript-helpers";
import { paths } from "../lib/api";
import { FetchResponse, MaybeOptionalInit } from "openapi-fetch";
type InitParam<Init> =
RequiredKeysOf<Init> extends never
? [(Init & { [key: string]: unknown })?]
: [Init & { [key: string]: unknown }];
export function useViewTransitioningFetch<
Path extends PathsWithMethod<paths, "get">,
Init extends MaybeOptionalInit<paths[Path], "get">,
>(
path: Path,
...init: InitParam<Init>
): Omit<
FetchResponse<paths[Path]["get"], Init, "application/json">,
"response"
>;
export function useFetch<
Path extends PathsWithMethod<paths, "get">,
Init extends MaybeOptionalInit<paths[Path], "get">,
>(
path: Path,
...init: InitParam<Init>
): Omit<
FetchResponse<paths[Path]["get"], Init, "application/json">,
"response"
>;

@ -0,0 +1,65 @@
// Using a .js & .d.ts file to make the types work well at use sites
// and make TypeScript, IDE, and eslint happy.
//
// Important to use `useMemo` on any options object passed in
// since it is used as a dependency to re-fetch
import { useBackend } from "./backend.ts";
import { useEffect, useState } from "react";
export function useFetch(path, options) {
const { client } = useBackend();
const [response, setResponse] = useState({});
useEffect(() => {
const abortController = new AbortController();
void doFetch(client, path, options, abortController, setResponse);
return () => {
abortController.abort();
};
}, [client, path, options]);
return { data: response.data, error: response.error };
}
export function useViewTransitioningFetch(path, options) {
const { client } = useBackend();
const [response, setResponse] = useState({});
useEffect(() => {
const abortController = new AbortController();
withViewTransition(function () {
return doFetch(client, path, options, abortController, setResponse);
});
return () => {
abortController.abort();
};
}, [client, path, options]);
return { data: response.data, error: response.error };
}
async function doFetch(client, path, options, abortController, setResponse) {
try {
const response = await client.GET(path, {
...options,
signal: abortController.signal,
});
setResponse(response);
} catch (error) {
if (abortController.signal.aborted) {
return;
}
setResponse({ error });
}
}
function withViewTransition(f) {
if (document.startViewTransition) void document.startViewTransition(f);
else void f();
}

@ -0,0 +1,25 @@
import { useProfile } from "./profile.ts";
import { messages } from "../lib/messages.ts";
type MessageType = typeof messages;
type MessageKeys = keyof MessageType;
type MessageParams<T extends MessageKeys> = MessageType[T]["en"] extends (
...args: any // eslint-disable-line @typescript-eslint/no-explicit-any
) => any // eslint-disable-line @typescript-eslint/no-explicit-any
? Parameters<MessageType[T]["en"]>
: [];
export function useI18n() {
const profile = useProfile();
const lang = profile.language;
return function <T extends MessageKeys>(
key: T,
...args: MessageParams<T>
): string {
const message = messages[key][lang] ?? messages[key]["en"];
// @ts-expect-error see https://stackoverflow.com/a/75086839
return typeof message === "function" ? message(...args) : message;
};
}

@ -0,0 +1,15 @@
import { createContext, useContext } from "react";
export type Profile = {
name: string;
language: "en" | "sv";
};
export const ProfileContext = createContext<Profile>({
name: "Unknown",
language: "en",
});
export function useProfile() {
return useContext(ProfileContext);
}

11
frontend/src/index.css Normal file

@ -0,0 +1,11 @@
:root {
--color-su-primary: #002f5f;
--color-su-primary-80: #33587f;
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
}

529
frontend/src/lib/api.d.ts vendored Normal file

@ -0,0 +1,529 @@
/**
* This file was auto-generated by openapi-typescript.
* Do not make direct changes to the file.
*/
export interface paths {
"/profile": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get: operations["getProfile"];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/test": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get: operations["helloWorld"];
put: operations["helloWorld_2"];
post: operations["helloWorld_1"];
delete: operations["helloWorld_3"];
options: operations["helloWorld_6"];
head: operations["helloWorld_5"];
patch: operations["helloWorld_4"];
trace?: never;
};
"/test/name": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get: operations["name"];
put: operations["name_2"];
post: operations["name_1"];
delete: operations["name_3"];
options: operations["name_6"];
head: operations["name_5"];
patch: operations["name_4"];
trace?: never;
};
"/test/email": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get: operations["email"];
put: operations["email_2"];
post: operations["email_1"];
delete: operations["email_3"];
options: operations["email_6"];
head: operations["email_5"];
patch: operations["email_4"];
trace?: never;
};
}
export type webhooks = Record<string, never>;
export interface components {
schemas: {
Profile: {
name: string;
/** @enum {string} */
language: "sv" | "en";
};
};
responses: never;
parameters: never;
requestBodies: never;
headers: never;
pathItems: never;
}
export type $defs = Record<string, never>;
export interface operations {
getProfile: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Profile"];
};
};
};
};
helloWorld: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": string;
};
};
};
};
helloWorld_2: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": string;
};
};
};
};
helloWorld_1: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": string;
};
};
};
};
helloWorld_3: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": string;
};
};
};
};
helloWorld_6: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": string;
};
};
};
};
helloWorld_5: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": string;
};
};
};
};
helloWorld_4: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": string;
};
};
};
};
name: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": string;
};
};
};
};
name_2: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": string;
};
};
};
};
name_1: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": string;
};
};
};
};
name_3: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": string;
};
};
};
};
name_6: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": string;
};
};
};
};
name_5: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": string;
};
};
};
};
name_4: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": string;
};
};
};
};
email: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": string;
};
};
};
};
email_2: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": string;
};
};
};
};
email_1: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": string;
};
};
};
};
email_3: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": string;
};
};
};
};
email_6: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": string;
};
};
};
};
email_5: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": string;
};
};
};
};
email_4: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": string;
};
};
};
};
}

@ -0,0 +1,23 @@
type AllLanguages<T> = { en: T; sv: T };
export const messages = {
"Home screen": {
en: "Home",
sv: "Aktuellt",
},
"Here you can see the latest and greatest": {
en: "Here you can see the latest and greatest",
sv: "Här ser du allt som är aktuellt",
},
Profile: {
en: "Profile",
sv: "Profil",
},
"Log out": {
en: "Log out",
sv: "Logga ut",
},
} as const satisfies Record<
string,
AllLanguages<string> | AllLanguages<(...args: any) => string> // eslint-disable-line @typescript-eslint/no-explicit-any
>;

10
frontend/src/main.tsx Normal file

@ -0,0 +1,10 @@
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App.tsx";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<App />
</StrictMode>,
);

@ -0,0 +1,3 @@
export default function Footer() {
return <footer></footer>;
}

@ -0,0 +1,14 @@
import "./header.css";
import suLogo from "../assets/SU_logo_optimized.svg";
import { useProfile } from "../hooks/profile.ts";
export default function Header() {
const profile = useProfile();
return (
<header>
<img src={suLogo} alt="Stockholm University" />
<span>{profile.name}</span>
</header>
);
}

@ -0,0 +1,11 @@
import { useI18n } from "../hooks/i18n.ts";
export default function Home() {
const i18n = useI18n();
return (
<>
<h1>{i18n("Home screen")}</h1>
<p>{i18n("Here you can see the latest and greatest")}</p>
</>
);
}

@ -0,0 +1,20 @@
import Header from "./Header.tsx";
import Menu from "./Menu.tsx";
import { Outlet } from "react-router";
import Footer from "./Footer.tsx";
import "./layout.css";
export default function Layout() {
return (
<>
<div id={"layout"}>
<Header />
<Menu />
<main>
<Outlet />
</main>
<Footer />
</div>
</>
);
}

@ -0,0 +1,25 @@
import { NavLink } from "react-router";
import "./menu.css";
import { useState } from "react";
import { useI18n } from "../hooks/i18n.ts";
export default function Menu() {
const [subMenuVisible, setSubMenuVisible] = useState(false);
const i18n = useI18n();
return (
<menu className={"main"}>
<li>
<NavLink to={"/"}>{i18n("Home screen")}</NavLink>
</li>
<li>
<button onClick={() => setSubMenuVisible((current) => !current)}>
<span>| | |</span>
</button>
<menu className={"sub " + (subMenuVisible ? "visible" : "")}>
<li>{i18n("Profile")}</li>
<li>{i18n("Log out")}</li>
</menu>
</li>
</menu>
);
}

@ -0,0 +1,12 @@
header {
height: 4em;
background-color: var(--color-su-primary);
display: flex;
justify-content: space-between;
align-items: center;
padding-right: 1em;
color: white;
img {
max-height: 100%;
}
}

@ -0,0 +1,6 @@
#layout {
min-height: 100vh;
}
main {
padding: 0 1em;
}

@ -0,0 +1,57 @@
menu.main {
position: fixed;
bottom: 0;
background-color: var(--color-su-primary);
margin: 0;
padding: 0;
left: 0;
right: 0;
display: flex;
li {
list-style: none;
margin: 0;
flex-basis: 1px;
&:last-child {
margin-left: auto;
}
}
a,
button {
color: white;
padding: 1.3em;
display: block;
margin: 0;
height: 100%;
width: 100%;
min-width: 5em;
}
button {
background: none;
border: none;
span {
transform: rotate(90deg);
display: block;
}
}
menu.sub.visible {
display: flex;
}
menu.sub {
flex-direction: column-reverse;
display: none;
position: absolute;
bottom: 100%;
right: 0;
padding: 0;
margin: 0;
color: white;
background-color: var(--color-su-primary-80);
li {
min-width: 10em;
padding: 1em;
border: 1px solid black;
border-bottom: none;
}
}
}

1
frontend/src/vite-env.d.ts vendored Normal file

@ -0,0 +1 @@
/// <reference types="vite/client" />

@ -0,0 +1,26 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2023",
"useDefineForClassFields": true,
"lib": ["ES2023", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src"]
}

7
frontend/tsconfig.json Normal file

@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

@ -0,0 +1,24 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2022",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}

7
frontend/vite.config.ts Normal file

@ -0,0 +1,7 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
});