chore: first build

This commit is contained in:
Khairul Hidayat 2024-08-17 04:55:01 +07:00
parent 61a07068d5
commit e1d2274bfb
37 changed files with 537 additions and 59 deletions

5
.dockerignore Normal file
View File

@ -0,0 +1,5 @@
node_modules
.git
.gitignore
*.md
dist

1
.gitignore vendored
View File

@ -25,3 +25,4 @@ dist-ssr
.env*
!.env.example
docker-compose.*.yml

34
Dockerfile Normal file
View File

@ -0,0 +1,34 @@
FROM node:20-slim AS base
WORKDIR /app
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
FROM base AS frontend
COPY package.json pnpm-lock.yaml ./
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
COPY . .
RUN pnpm run build
FROM base AS backend-deps
COPY backend/package.json backend/pnpm-lock.yaml ./
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install -prod --frozen-lockfile
COPY backend .
FROM oven/bun:alpine AS backend
WORKDIR /app
COPY --from=backend-deps /app .
RUN bun run build
FROM oven/bun:alpine
WORKDIR /app
ENV NODE_ENV=production
ENV DIST_ROOT=./dist
COPY --from=frontend /app/dist /app/dist
COPY --from=backend /app/dist /app
ENTRYPOINT [ "bun" , "run", "main.js" ]

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Khairul Hidayat
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

148
README.md
View File

@ -1,50 +1,122 @@
# React + TypeScript + Vite
# Garage Web UI
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
[![image](misc/img/garage-webui.png)](misc/img/garage-webui.png)
Currently, two official plugins are available:
A simple admin web UI for [Garage](https://garagehq.deuxfleurs.fr/), a self-hosted, S3-compatible, distributed object storage service.
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
[ [Screenshots](misc/SCREENSHOTS.md) | [Install Garage](https://garagehq.deuxfleurs.fr/documentation/quick-start/) | [Garage Git](https://git.deuxfleurs.fr/Deuxfleurs/garage) ]
## Expanding the ESLint configuration
## Installation
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
The Garage Web UI is available as a Docker image. You can install it using the command line or with Docker Compose.
- Configure the top-level `parserOptions` property like this:
### Docker CLI
```js
export default tseslint.config({
languageOptions: {
// other options...
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
},
})
```sh
$ docker run -p 3909:3909 -v ./garage.toml:/etc/garage.toml --restart unless-stopped --name garage-webui khairul169/garage-webui:latest
```
- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
- Optionally add `...tseslint.configs.stylisticTypeChecked`
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config:
### Docker Compose
```js
// eslint.config.js
import react from 'eslint-plugin-react'
If you install Garage using Docker, you can install this web UI alongside Garage as follows:
export default tseslint.config({
// Set the react version
settings: { react: { version: '18.3' } },
plugins: {
// Add the react plugin
react,
},
rules: {
// other rules...
// Enable its recommended rules
...react.configs.recommended.rules,
...react.configs['jsx-runtime'].rules,
},
})
```yml
services:
garage:
image: dxflrs/garage:v1.0.0
container_name: garage
volumes:
- ./garage.toml:/etc/garage.toml
- ./meta:/var/lib/garage/meta
- ./data:/var/lib/garage/data
restart: unless-stopped
network_mode: host
webui:
image: khairul169/garage-webui:latest
container_name: garage-webui
restart: unless-stopped
volumes:
- ./garage.toml:/etc/garage.toml
ports:
- 3909:3909
```
### Configuration
To simplify installation, the Garage Web UI uses values from the Garage configuration, such as `rpc_public_addr`, `admin.admin_token`, `s3_web.root_domain`, etc.
Example content of `config.toml`:
```toml
metadata_dir = "/var/lib/garage/meta"
data_dir = "/var/lib/garage/data"
db_engine = "sqlite"
metadata_auto_snapshot_interval = "6h"
replication_factor = 3
compression_level = 2
rpc_bind_addr = "[::]:3901"
rpc_public_addr = "localhost:3901" # Required
rpc_secret = "YOUR_RPC_SECRET_HERE"
[s3_api]
s3_region = "garage"
api_bind_addr = "[::]:3900"
root_domain = ".s3.domain.com"
[s3_web] # Optional, if you want to expose bucket as web
bind_addr = "[::]:3902"
root_domain = ".web.domain.com"
index = "index.html"
[admin] # Required
api_bind_addr = "[::]:3903"
admin_token = "YOUR_ADMIN_TOKEN_HERE"
metrics_token = "YOUR_METRICS_TOKEN_HERE"
```
However, if it fails to load, you can set these environment variables instead:
- `CONFIG_PATH`: Path to the Garage `config.toml` file. Defaults to `/etc/garage.toml`.
- `API_BASE_URL`: Garage admin API endpoint URL.
- `API_ADMIN_KEY`: Admin API key.
### Running
Once your instance of Garage Web UI is started, you can open the web UI at http://your-ip:3909. You can place it behind a reverse proxy to secure it with SSL.
## Development
This project is bootstrapped using TypeScript, Bun, React, and Hono. If you want to build it yourself or add additional features, follow these steps:
### Setup
```sh
$ git clone https://github.com/khairul169/garage-webui.git
$ cd garage-webui && pnpm install
$ cd backend && pnpm install && cd ..
```
### Running
Start both the client and server concurrently:
```sh
$ pnpm run dev # or npm run dev
```
Or start each instance separately:
```sh
$ pnpm run dev:client
$ cd backend
$ pnpm run dev:server
```
## Troubleshooting
Make sure you are using the latest version of Garage. If the data cannot be loaded, please check whether your instance of Garage has the admin API enabled and the ports are accessible.
If you encounter any problems, please do not hesitate to submit an issue [here](https://github.com/khairul169/garage-webui/issues). You can describe the problem and attach the error logs.

2
backend/lib/consts.ts Normal file
View File

@ -0,0 +1,2 @@
//
export const __PROD = import.meta.env.NODE_ENV === "production";

View File

@ -1,4 +1,16 @@
import type { Config } from "../types/garage";
import { readTomlFile } from "./utils";
export const config = readTomlFile<Config>(process.env.CONFIG_PATH);
const CONFIG_PATH = process.env.CONFIG_PATH || "/etc/garage.toml";
export const config = await readTomlFile<Config>(CONFIG_PATH);
if (!config?.rpc_public_addr) {
throw new Error(
"Cannot load garage config! Missing `rpc_public_addr` in config file."
);
}
if (!config?.admin?.admin_token) {
throw new Error("Missing `admin.admin_token` in config.");
}

View File

@ -3,7 +3,8 @@ import { API_ADMIN_KEY, API_BASE_URL } from "./api";
export const proxyApi = async (c: Context) => {
const url = new URL(c.req.url);
const reqUrl = new URL(API_BASE_URL + url.pathname + url.search);
const pathname = url.pathname.replace(/\/api\//, "/");
const reqUrl = new URL(API_BASE_URL + pathname + url.search);
try {
const headers = c.req.raw.headers;

View File

@ -1,9 +1,14 @@
import fs from "node:fs";
import toml from "toml";
export const readTomlFile = <T = any>(path?: string | null) => {
if (!path || !fs.existsSync(path)) {
export const readTomlFile = async <T = any>(path?: string | null) => {
if (!path) {
return undefined;
}
return toml.parse(fs.readFileSync(path, "utf8")) as T;
const file = Bun.file(path);
if (!(await file.exists())) {
return undefined;
}
return toml.parse(await file.text()) as T;
};

View File

@ -1,20 +1,34 @@
import { Hono } from "hono";
import { logger } from "hono/logger";
import { serveStatic } from "hono/bun";
import router from "./routes";
import { proxyApi } from "./lib/proxy-api";
import { __PROD } from "./lib/consts";
const HOST = import.meta.env.HOST || "0.0.0.0";
const PORT = Number(import.meta.env.PORT) || 3909;
const DIST_ROOT = import.meta.env.DIST_ROOT || "./dist";
const app = new Hono();
app.use(logger());
// API router
app.route("/", router);
app.route("/api", router);
// Proxy to garage admin API
app.all("*", proxyApi);
if (__PROD) {
// Serve client dist
app.use(serveStatic({ root: DIST_ROOT }));
app.use(async (c, next) => {
try {
const file = Bun.file(DIST_ROOT + "/index.html");
return c.html(await file.text());
} catch (err) {
next();
}
});
console.log(`Listening on http://${HOST}:${PORT}`);
}
export default {
fetch: app.fetch,

View File

@ -3,7 +3,9 @@
"module": "main.ts",
"type": "module",
"scripts": {
"dev": "bun --watch main.ts"
"dev": "bun --watch main.ts",
"build": "bun build main.ts --minify --sourcemap --outdir ./dist",
"start": "NODE_ENV=production bun run ./dist/main.js"
},
"devDependencies": {
"@types/bun": "latest"

10
backend/pnpm-lock.yaml generated
View File

@ -14,6 +14,9 @@ importers:
toml:
specifier: ^3.0.0
version: 3.0.0
typescript:
specifier: ^5.0.0
version: 5.5.4
devDependencies:
'@types/bun':
specifier: latest
@ -40,6 +43,11 @@ packages:
toml@3.0.0:
resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==}
typescript@5.5.4:
resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==}
engines: {node: '>=14.17'}
hasBin: true
undici-types@5.26.5:
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
@ -66,4 +74,6 @@ snapshots:
toml@3.0.0: {}
typescript@5.5.4: {}
undici-types@5.26.5: {}

View File

@ -1,10 +1,13 @@
import { Hono } from "hono";
import { buckets } from "./buckets";
import { configRoute } from "./config";
import { proxyApi } from "../lib/proxy-api";
const router = new Hono()
//
.route("/config", configRoute)
.route("/buckets", buckets);
.route("/buckets", buckets)
.all("*", proxyApi);
export default router;

21
docker-compose.yml Normal file
View File

@ -0,0 +1,21 @@
services:
garage:
image: dxflrs/garage:v1.0.0
container_name: garage
volumes:
- ./garage.toml:/etc/garage.toml
- ./meta:/var/lib/garage/meta
- ./data:/var/lib/garage/data
restart: unless-stopped
network_mode: host
webui:
image: khairul169/garage-webui:latest
container_name: garage-webui
restart: unless-stopped
environment:
- CONFIG_PATH=/etc/garage.toml
volumes:
- ./garage.toml:/etc/garage.toml
ports:
- 3909:3909

View File

@ -1,10 +1,15 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en" data-theme="pastel">
<head>
<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>
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="manifest" href="/site.webmanifest" />
<title>Garage Web UI</title>
</head>
<body>
<div id="root"></div>

8
misc/SCREENSHOTS.md Normal file
View File

@ -0,0 +1,8 @@
# Garage Web UI Screenshots
[![image](img/home.png)](img/home.png)
[![image](img/cluster.png)](img/cluster.png)
[![image](img/buckets.png)](img/buckets.png)
[![image](img/buckets-overview.png)](img/buckets-overview.png)
[![image](img/buckets-permissions.png)](img/buckets-permissions.png)
[![image](img/keys.png)](img/keys.png)

11
misc/build.sh Executable file
View File

@ -0,0 +1,11 @@
#!/bin/sh
set -e
IMAGE_NAME="khairul169/garage-webui"
PACKAGE_VERSION=$(cat package.json | grep \"version\" | cut -d'"' -f 4)
echo "Building version $PACKAGE_VERSION"
docker buildx build --platform linux/amd64,linux/arm64 \
-t "$IMAGE_NAME:latest" -t "$IMAGE_NAME:$PACKAGE_VERSION" --push .

BIN
misc/img/buckets-overview.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
misc/img/buckets-permissions.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
misc/img/buckets.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
misc/img/cluster.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

BIN
misc/img/garage-webui.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

BIN
misc/img/home.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

BIN
misc/img/keys.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

@ -1,13 +1,15 @@
{
"name": "garage-webui",
"private": true,
"version": "0.0.0",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"dev:client": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
"preview": "vite preview",
"dev:server": "cd backend && npm run dev",
"dev": "concurrently \"npm run dev:client\" \"npm run dev:server\""
},
"dependencies": {
"@hookform/resolvers": "^3.9.0",
@ -32,6 +34,7 @@
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react-swc": "^3.5.0",
"autoprefixer": "^10.4.20",
"concurrently": "^8.2.2",
"daisyui": "^4.12.10",
"eslint": "^9.8.0",
"eslint-plugin-react-hooks": "^5.1.0-rc.0",

117
pnpm-lock.yaml generated
View File

@ -69,6 +69,9 @@ importers:
autoprefixer:
specifier: ^10.4.20
version: 10.4.20(postcss@8.4.41)
concurrently:
specifier: ^8.2.2
version: 8.2.2
daisyui:
specifier: ^4.12.10
version: 4.12.10(postcss@8.4.41)
@ -772,6 +775,10 @@ packages:
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
engines: {node: '>= 8.10.0'}
cliui@8.0.1:
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
engines: {node: '>=12'}
clsx@2.1.1:
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
engines: {node: '>=6'}
@ -796,6 +803,11 @@ packages:
concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
concurrently@8.2.2:
resolution: {integrity: sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==}
engines: {node: ^14.13.0 || >=16.0.0}
hasBin: true
convert-source-map@1.9.0:
resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==}
@ -826,6 +838,10 @@ packages:
resolution: {integrity: sha512-jp1RAuzbHhGdXmn957Z2XsTZStXGHzFfF0FgIOZj3Wv9sH7OZgLfXTRZNfKVYxltGUOBsG1kbWAdF5SrqjebvA==}
engines: {node: '>=16.9.0'}
date-fns@2.30.0:
resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
engines: {node: '>=0.11'}
debug@4.3.6:
resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==}
engines: {node: '>=6.0'}
@ -992,6 +1008,10 @@ packages:
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
get-caller-file@2.0.5:
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
engines: {node: 6.* || 8.* || >= 10.*}
glob-parent@5.1.2:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'}
@ -1140,6 +1160,9 @@ packages:
lodash.merge@4.6.2:
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
lodash@4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
loose-envify@1.4.0:
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
hasBin: true
@ -1380,6 +1403,10 @@ packages:
regenerator-runtime@0.14.1:
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'}
resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
@ -1400,6 +1427,9 @@ packages:
run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
rxjs@7.8.1:
resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==}
scheduler@0.23.2:
resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}
@ -1416,6 +1446,9 @@ packages:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
shell-quote@1.8.1:
resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==}
signal-exit@4.1.0:
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
engines: {node: '>=14'}
@ -1438,6 +1471,9 @@ packages:
resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==}
engines: {node: '>=0.10.0'}
spawn-command@0.0.2:
resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==}
string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
@ -1474,6 +1510,10 @@ packages:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'}
supports-color@8.1.1:
resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
engines: {node: '>=10'}
supports-preserve-symlinks-flag@1.0.0:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
@ -1504,6 +1544,10 @@ packages:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
tree-kill@1.2.2:
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
hasBin: true
ts-api-utils@1.3.0:
resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==}
engines: {node: '>=16'}
@ -1513,6 +1557,9 @@ packages:
ts-interface-checker@0.1.13:
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
tslib@2.6.3:
resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==}
type-check@0.4.0:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
@ -1608,6 +1655,10 @@ packages:
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
engines: {node: '>=12'}
y18n@5.0.8:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'}
yaml@1.10.2:
resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
engines: {node: '>= 6'}
@ -1617,6 +1668,14 @@ packages:
engines: {node: '>= 14'}
hasBin: true
yargs-parser@21.1.1:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'}
yargs@17.7.2:
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
engines: {node: '>=12'}
yocto-queue@0.1.0:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
@ -2260,6 +2319,12 @@ snapshots:
optionalDependencies:
fsevents: 2.3.3
cliui@8.0.1:
dependencies:
string-width: 4.2.3
strip-ansi: 6.0.1
wrap-ansi: 7.0.0
clsx@2.1.1: {}
color-convert@1.9.3:
@ -2278,6 +2343,18 @@ snapshots:
concat-map@0.0.1: {}
concurrently@8.2.2:
dependencies:
chalk: 4.1.2
date-fns: 2.30.0
lodash: 4.17.21
rxjs: 7.8.1
shell-quote: 1.8.1
spawn-command: 0.0.2
supports-color: 8.1.1
tree-kill: 1.2.2
yargs: 17.7.2
convert-source-map@1.9.0: {}
cosmiconfig@7.1.0:
@ -2314,6 +2391,10 @@ snapshots:
transitivePeerDependencies:
- postcss
date-fns@2.30.0:
dependencies:
'@babel/runtime': 7.25.0
debug@4.3.6:
dependencies:
ms: 2.1.2
@ -2507,6 +2588,8 @@ snapshots:
function-bind@1.1.2: {}
get-caller-file@2.0.5: {}
glob-parent@5.1.2:
dependencies:
is-glob: 4.0.3
@ -2631,6 +2714,8 @@ snapshots:
lodash.merge@4.6.2: {}
lodash@4.17.21: {}
loose-envify@1.4.0:
dependencies:
js-tokens: 4.0.0
@ -2853,6 +2938,8 @@ snapshots:
regenerator-runtime@0.14.1: {}
require-directory@2.1.1: {}
resolve-from@4.0.0: {}
resolve@1.22.8:
@ -2889,6 +2976,10 @@ snapshots:
dependencies:
queue-microtask: 1.2.3
rxjs@7.8.1:
dependencies:
tslib: 2.6.3
scheduler@0.23.2:
dependencies:
loose-envify: 1.4.0
@ -2901,6 +2992,8 @@ snapshots:
shebang-regex@3.0.0: {}
shell-quote@1.8.1: {}
signal-exit@4.1.0: {}
slash@3.0.0: {}
@ -2914,6 +3007,8 @@ snapshots:
source-map@0.5.7: {}
spawn-command@0.0.2: {}
string-width@4.2.3:
dependencies:
emoji-regex: 8.0.0
@ -2956,6 +3051,10 @@ snapshots:
dependencies:
has-flag: 4.0.0
supports-color@8.1.1:
dependencies:
has-flag: 4.0.0
supports-preserve-symlinks-flag@1.0.0: {}
tailwind-merge@2.5.2: {}
@ -3003,12 +3102,16 @@ snapshots:
dependencies:
is-number: 7.0.0
tree-kill@1.2.2: {}
ts-api-utils@1.3.0(typescript@5.5.4):
dependencies:
typescript: 5.5.4
ts-interface-checker@0.1.13: {}
tslib@2.6.3: {}
type-check@0.4.0:
dependencies:
prelude-ls: 1.2.1
@ -3077,10 +3180,24 @@ snapshots:
string-width: 5.1.2
strip-ansi: 7.1.0
y18n@5.0.8: {}
yaml@1.10.2: {}
yaml@2.5.0: {}
yargs-parser@21.1.1: {}
yargs@17.7.2:
dependencies:
cliui: 8.0.1
escalade: 3.1.2
get-caller-file: 2.0.5
require-directory: 2.1.1
string-width: 4.2.3
y18n: 5.0.8
yargs-parser: 21.1.1
yocto-queue@0.1.0: {}
zod@3.23.8: {}

BIN
public/android-chrome-192x192.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
public/android-chrome-512x512.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

BIN
public/apple-touch-icon.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
public/favicon-16x16.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 735 B

BIN
public/favicon-32x32.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
public/favicon.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

1
public/site.webmanifest Executable file
View File

@ -0,0 +1 @@
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}

View File

@ -1 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 1.5 KiB

131
src/assets/garage-logo.svg Executable file
View File

@ -0,0 +1,131 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="30mm"
height="30mm"
viewBox="0 0 30 30"
version="1.1"
id="svg5"
sodipodi:docname="garage-logo.svg"
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="5.0701029"
inkscape:cx="75.738107"
inkscape:cy="78.20354"
inkscape:window-width="1920"
inkscape:window-height="1009"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg5" />
<defs
id="defs2" />
<path
id="path6"
d="m 18.252345,9.0579502 c -0.218792,0.024311 -0.461894,0 -0.680691,-0.072931 L 9.1116177,6.6998396 C 8.8928239,6.6269083 8.6740302,6.5296667 8.4795464,6.4081145 Z"
style="stroke-width:0.243104" />
<path
id="path26"
class="st3"
d="m 18.422521,12.558652 4.643297,-1.871904 c 0.09724,-0.04862 0.145863,-0.145862 0.121552,-0.267414 -0.02432,-0.04862 -0.04862,-0.09724 -0.121552,-0.121554 -0.534828,-0.218791 -1.215524,-0.4862076 -1.871903,-0.7536217 -0.121557,-0.04862 -1.264147,0.7779337 -1.361387,0.8508647 l -1.993457,1.604487 c -0.4619,0.437588 -0.04862,0.826556 0.58345,0.559142 z"
style="stroke-width:0.243104" />
<ellipse
id="circle28"
class="st3"
cx="15.067682"
cy="14.381929"
rx="2.4310451"
ry="2.4310422"
style="stroke-width:0.243104" />
<path
id="path6-0"
d="m 18.252345,9.0579502 c -0.218792,0.024311 -0.461894,0 -0.680691,-0.072931 L 9.1116177,6.6998396 C 8.8928239,6.6269083 8.6740302,6.5296667 8.4795464,6.4081145 Z"
style="stroke-width:0.243104" />
<g
id="g1"
style="stroke:#ffffff;stroke-opacity:1;stroke-linejoin:round;stroke-linecap:butt;paint-order:markers stroke fill;stroke-width:0.8;stroke-dasharray:none">
<path
id="path8"
class="st0"
d="m 5.3921192,23.425409 c 0.4836869,0.0057 0.9683846,-0.0121 1.4513623,0.01026 0.2797376,0.01631 0.5246598,0.244204 0.5357045,0.528822 0.014525,0.715514 0.00242,1.431989 0.00639,2.147882 0.00975,0.18178 -0.2937553,0.13971 -0.4176027,0.217322 C 6.2505828,26.527076 5.4892337,26.60307 4.7516897,26.487383 4.1256342,26.381378 3.5905644,25.93667 3.3273915,25.364822 2.9915288,24.650787 2.9376085,23.833058 3.020282,23.057781 c 0.07389,-0.647851 0.3650579,-1.282203 0.8753663,-1.70056 0.5504884,-0.483741 1.319811,-0.660967 2.0361625,-0.553729 0.5196872,0.06538 1.0240239,0.255395 1.4537643,0.555533 -0.049017,0.302734 -0.2236719,0.59045 -0.4862094,0.753622 -0.4701824,-0.345013 -1.0720551,-0.534813 -1.6553823,-0.471769 -0.5097053,0.0798 -0.9483078,0.472324 -1.0796061,0.972454 -0.1679185,0.593908 -0.1684748,1.229968 -0.063593,1.835175 0.08495,0.484701 0.3735586,0.980534 0.8684921,1.132012 0.468823,0.153492 0.9695032,0.05737 1.4438802,-0.0158 0.00585,-0.413445 -0.014399,-0.828053 0.016284,-1.240543 0.011286,-0.09692 -0.1751,-0.02652 -0.2530503,-0.04791 -0.2543952,-0.01394 -0.533292,0.04887 -0.7599644,-0.09724 -0.2168052,-0.202613 -0.082489,-0.513657 -0.024311,-0.753622 z"
style="stroke-width:0.8;stroke:#ffffff;stroke-opacity:1;stroke-linejoin:round;stroke-linecap:butt;paint-order:markers stroke fill;stroke-dasharray:none" />
<path
id="path10"
class="st0"
d="m 11.639907,25.953692 c -0.02432,0.09724 -0.07293,0.194483 -0.145865,0.291725 -0.04862,0.09724 -0.121552,0.170174 -0.194481,0.218794 -0.316036,-0.02432 -0.583453,-0.194482 -0.753627,-0.486208 -0.291725,0.316034 -0.7293107,0.51052 -1.1668987,0.51052 -0.4132803,0 -0.7293157,-0.121554 -0.9481099,-0.364657 -0.1944835,-0.243105 -0.3160359,-0.534831 -0.3160359,-0.850865 0,-0.461899 0.1458627,-0.802245 0.4375883,-1.021039 0.3403461,-0.243102 0.7293149,-0.364656 1.1425891,-0.340345 0.2431078,0 0.4862111,0 0.7050071,0.02432 v -0.243105 c 0,-0.437588 -0.194485,-0.632071 -0.6077645,-0.632071 -0.2917228,0 -0.7050023,0.09724 -1.2398317,0.291726 -0.1701732,-0.194483 -0.2674151,-0.4619 -0.2674151,-0.729314 0.5591402,-0.243103 1.1425926,-0.364656 1.7503523,-0.364656 0.364659,-0.02432 0.705004,0.09724 0.99673,0.340347 0.243105,0.218794 0.388965,0.55914 0.388965,1.021037 v 1.653109 c -0.02432,0.364657 0.04862,0.55914 0.218797,0.680691 z M 9.5978268,25.759209 c 0.3160366,-0.02432 0.6077622,-0.145862 0.8022472,-0.388968 v -0.705002 c -0.194485,-0.02432 -0.4132792,-0.02432 -0.6077645,-0.02432 -0.1944827,-0.02432 -0.3646542,0.04862 -0.5105168,0.170171 -0.1215542,0.121554 -0.170175,0.267417 -0.170175,0.437588 0,0.145863 0.048621,0.291726 0.1458637,0.413277 0.097243,0.04862 0.218794,0.09724 0.3403454,0.09724 z"
style="stroke-width:0.8;stroke:#ffffff;stroke-opacity:1;stroke-linejoin:round;stroke-linecap:butt;paint-order:markers stroke fill;stroke-dasharray:none" />
<path
id="path12"
class="st0"
d="m 12.296286,22.866269 c 0.02432,-0.09724 0.07293,-0.194482 0.145863,-0.291725 0.04862,-0.09724 0.121554,-0.170174 0.194483,-0.218794 0.413276,0.04862 0.729313,0.340345 0.850867,0.729313 0.218794,-0.486208 0.559139,-0.729313 1.04535,-0.729313 0.145863,0 0.316032,0.02432 0.461897,0.04862 0,0.340348 -0.07293,0.656382 -0.243103,0.948108 -0.121554,-0.02432 -0.243105,-0.04862 -0.364657,-0.04862 -0.340348,0 -0.58345,0.170172 -0.802247,0.534829 v 2.576905 c -0.170171,0.02432 -0.340345,0.04862 -0.48621,0.04862 -0.170169,0 -0.340343,-0.02432 -0.510517,-0.04862 v -2.722768 c 0,-0.388966 -0.09724,-0.656382 -0.291726,-0.826554 z"
style="stroke-width:0.8;stroke:#ffffff;stroke-opacity:1;stroke-linejoin:round;stroke-linecap:butt;paint-order:markers stroke fill;stroke-dasharray:none" />
<path
id="path14"
class="st0"
d="m 18.762867,25.953692 c -0.02432,0.09724 -0.07293,0.194483 -0.145863,0.291725 -0.04862,0.09724 -0.121551,0.170174 -0.194483,0.218794 -0.316036,-0.02432 -0.58345,-0.194482 -0.753627,-0.486208 -0.291723,0.316034 -0.729311,0.51052 -1.166901,0.51052 -0.413275,0 -0.729314,-0.121554 -0.948105,-0.364657 -0.194485,-0.243105 -0.316037,-0.534831 -0.316037,-0.850865 0,-0.461899 0.145863,-0.802245 0.437588,-1.021039 0.316034,-0.243102 0.729314,-0.364656 1.118282,-0.340345 0.243102,0 0.48621,0 0.705002,0.02432 v -0.243105 c 0,-0.437588 -0.194481,-0.632071 -0.607762,-0.632071 -0.291726,0 -0.705002,0.09724 -1.239833,0.291726 -0.170174,-0.194483 -0.267414,-0.4619 -0.267414,-0.729314 0.559139,-0.243103 1.14259,-0.364656 1.750352,-0.364656 0.364657,-0.02432 0.705005,0.09724 0.99673,0.340347 0.243103,0.218794 0.388965,0.55914 0.388965,1.021037 v 1.653109 c 0,0.364657 0.09724,0.55914 0.243106,0.680691 z m -2.042078,-0.194483 c 0.316034,-0.02432 0.607762,-0.145862 0.802245,-0.388968 v -0.705002 c -0.194483,-0.02432 -0.413279,-0.02432 -0.607762,-0.02432 -0.194483,-0.02432 -0.364659,0.04862 -0.510519,0.170171 -0.121552,0.121554 -0.170172,0.267417 -0.170172,0.437588 0,0.145863 0.04862,0.291726 0.145863,0.413277 0.09724,0.04862 0.218791,0.09724 0.340345,0.09724 z"
style="stroke-width:0.8;stroke:#ffffff;stroke-opacity:1;stroke-linejoin:round;stroke-linecap:butt;paint-order:markers stroke fill;stroke-dasharray:none" />
<path
id="path16"
class="st0"
d="m 19.759597,25.929383 c -0.243106,-0.364657 -0.364659,-0.875176 -0.364659,-1.531558 0,-0.656382 0.145865,-1.166899 0.461899,-1.531556 0.267416,-0.340345 0.680693,-0.559139 1.118281,-0.559139 0.413279,0 0.802245,0.145863 1.09397,0.413277 0.145863,-0.194483 0.388971,-0.316037 0.632071,-0.340346 0.09724,0.04862 0.170174,0.145863 0.218797,0.218794 0.04862,0.09724 0.09724,0.170172 0.145862,0.291723 -0.145862,0.121554 -0.218796,0.364657 -0.218796,0.705005 v 2.285178 c 0,0.850864 -0.145863,1.458626 -0.413275,1.823281 -0.267416,0.364656 -0.753627,0.53483 -1.385697,0.53483 -0.486211,0 -0.996728,-0.09724 -1.458627,-0.267414 0,-0.267416 0.07293,-0.534831 0.243105,-0.729313 0.340345,0.170174 0.705002,0.243105 1.093968,0.243105 0.364659,0 0.607764,-0.09724 0.729316,-0.267417 0.145862,-0.218794 0.218794,-0.486208 0.194482,-0.753622 v -0.316036 c -0.291725,0.243105 -0.63207,0.388968 -1.021038,0.388968 -0.437589,-0.02432 -0.850868,-0.243106 -1.069659,-0.60776 z m 2.066389,-0.656382 v -1.871903 c -0.170174,-0.194483 -0.437589,-0.316035 -0.705005,-0.340346 -0.218794,0 -0.437586,0.145863 -0.534831,0.340346 -0.145862,0.316036 -0.218794,0.656382 -0.194483,0.996727 0,0.437588 0.04862,0.753625 0.170172,0.948107 0.07293,0.170172 0.267417,0.291726 0.461899,0.316035 0.340348,0 0.632074,-0.145863 0.802248,-0.388966 z"
style="stroke-width:0.8;stroke:#ffffff;stroke-opacity:1;stroke-linejoin:round;stroke-linecap:butt;paint-order:markers stroke fill;stroke-dasharray:none" />
<path
id="path18-7"
class="st0"
d="m 26.979799,24.73817 h -2.260871 c 0,0.267417 0.07293,0.51052 0.218794,0.729314 0.121551,0.170174 0.340345,0.267414 0.656379,0.267414 0.36466,-0.02432 0.729316,-0.121551 1.069662,-0.291725 0.170174,0.170174 0.267414,0.388968 0.291725,0.607762 -0.461897,0.316034 -0.996727,0.486208 -1.555869,0.461896 -0.60776,0 -1.04535,-0.194482 -1.312762,-0.583448 -0.267419,-0.388968 -0.388968,-0.899487 -0.388968,-1.531558 0,-0.632071 0.14586,-1.14259 0.437588,-1.531556 0.291723,-0.388968 0.753622,-0.607762 1.23983,-0.58345 0.534833,0 0.94811,0.170174 1.239836,0.486208 0.291725,0.340345 0.461896,0.777933 0.437588,1.239833 0,0.243102 -0.02432,0.486208 -0.07293,0.72931 z M 25.42393,23.012132 c -0.437588,0 -0.680693,0.340346 -0.705002,0.996728 h 1.410004 v -0.09724 c 0,-0.218794 -0.04862,-0.461897 -0.170171,-0.656382 -0.121552,-0.170172 -0.340348,-0.243103 -0.534831,-0.243103 z"
style="stroke-width:0.8;stroke:#ffffff;stroke-opacity:1;stroke-linejoin:round;stroke-linecap:butt;paint-order:markers stroke fill;stroke-dasharray:none" />
</g>
<path
id="path24-3-6-9"
class="st4"
d="m 14.970437,1.7648241 c -0.364657,0 -0.729316,0.072931 -1.045352,0.2187938 L 3.179868,6.7484604 c -0.1944833,0.072931 -0.2917252,0.3160354 -0.2187938,0.5105189 0,0 0,0 0,0 0,0.024311 0,0.024311 0.024311,0.048621 L 4.7114273,10.71106 H 6.2186752 L 5.9998812,9.519847 C 5.9755699,9.4226069 5.8297079,8.7419148 5.6838453,8.2557065 l 5.0322627,1.5801772 c 0.07293,0.3160363 0.145863,0.6077593 0.267417,0.8751763 h 8.143998 c 0.09724,-0.267417 0.170172,-0.55914 0.218794,-0.8751763 l 5.007952,-1.5558669 c -0.145861,0.4862083 -0.291723,1.1669014 -0.316035,1.2641415 l -0.218796,1.1912097 h 1.507249 l 1.701732,-3.3791471 0.02432,-0.024311 0.02432,-0.048621 c 0,0 0,0 0,0 0.02432,-0.048621 0.02432,-0.072931 0.02432,-0.1215521 0,-0.170173 -0.09724,-0.3160354 -0.243105,-0.3889668 L 16.113047,2.0079277 C 15.724077,1.8377547 15.35942,1.7405131 14.970455,1.7648234 Z"
style="stroke-width:0.243104" />
<path
id="path24-3-2"
class="st0"
d="m 14.970437,4.0013828 c -0.364657,0 -0.729316,0.072931 -1.045352,0.2187938 L 3.179868,8.9850191 c -0.1944833,0.072931 -0.2917252,0.3160364 -0.2187938,0.5105192 0,0 0,0 0,0 0,0.024321 0,0.024321 0.024311,0.04862 L 3.9091823,11.391751 H 5.9026395 C 5.8297079,11.124336 5.7567764,10.8083 5.6595347,10.492266 l 2.844323,0.899485 H 21.485638 l 2.844319,-0.899485 c -0.09724,0.291725 -0.170169,0.63207 -0.2431,0.899485 h 1.993452 l 0.923799,-1.8232814 0.02432,-0.024321 0.02432,-0.04862 c 0,0 0,0 0,0 0.02432,-0.04862 0.02432,-0.072931 0.02432,-0.1215514 0,-0.170174 -0.09724,-0.3160367 -0.243105,-0.3889677 L 16.088718,4.2201766 C 15.724059,4.0743142 15.359403,4.0013828 14.970437,4.0013828 Z"
style="stroke-width:0.243104" />
<path
id="path24-0"
class="st4"
d="m 27.077039,11.659165 c 0,0.04862 0,0.07293 -0.02432,0.121554 0,0 0,0 0,0 l -0.02432,0.04862 v 0 l -0.02432,0.02432 -2.601219,5.153809 c -0.364654,0.705002 -1.021034,0.680691 -0.923791,-0.02432 l 0.534825,-2.917251 c 0.02432,-0.09724 0.170174,-0.777933 0.316037,-1.264141 l -5.007969,1.555867 c -0.777933,5.1295 -7.536236,5.202431 -8.630209,0 l -5.0322664,-1.58018 c 0.1458629,0.486211 0.2917256,1.166902 0.3160361,1.264145 l 0.5348297,2.917248 c 0.1215524,0.729313 -0.5591402,0.753625 -0.9237969,0.02432 l -2.6254813,-5.17813 c 0,-0.0243 -0.024311,-0.0243 -0.024311,-0.04862 -0.097242,-0.194482 0,-0.437588 0.1944835,-0.510519 0,0 0,0 0,0 L 13.876467,6.4810456 c 0.680691,-0.2917249 1.482936,-0.2917249 2.163629,0 l 10.745218,4.7648424 c 0.194485,0.09724 0.291725,0.243105 0.291725,0.413277 z"
style="stroke-width:0.243104" />
<path
id="path26-2"
class="st0"
d="m 18.422521,12.558652 4.643297,-1.871904 c 0.09724,-0.04862 0.145863,-0.145862 0.121552,-0.267414 -0.02432,-0.04862 -0.04862,-0.09724 -0.121552,-0.121554 -0.534828,-0.218791 -1.215524,-0.4862076 -1.871903,-0.7536217 -0.121557,-0.04862 -1.264147,0.7779337 -1.361387,0.8508647 l -1.993457,1.604487 c -0.4619,0.437588 -0.04862,0.826556 0.58345,0.559142 z"
style="stroke-width:0.243104" />
<ellipse
id="circle28-3"
class="st0"
cx="15.067682"
cy="14.381929"
rx="2.4310451"
ry="2.4310422"
style="stroke-width:0.243104" />
<style
type="text/css"
id="style2346">
.st0{fill:#4E4E4E;}
.st1{fill:#FFD952;}
.st2{fill:#49C8FA;}
.st3{fill:#45C8FF;}
.st4{fill:#FF9329;}
.st5{fill:#3B2100;}
.st6{fill:#C3C3C3;}
</style>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -11,6 +11,7 @@ import { Link, useLocation } from "react-router-dom";
import Button from "../ui/button";
import { themes } from "@/app/themes";
import appStore from "@/stores/app-store";
import garageLogo from "@/assets/garage-logo.svg";
const pages = [
{ icon: LayoutDashboard, title: "Dashboard", path: "/", exact: true },
@ -26,7 +27,7 @@ const Sidebar = () => {
<aside className="bg-base-100 border-r border-base-300/30 w-[80%] md:w-[250px] flex flex-col items-stretch overflow-hidden h-full">
<div className="p-4">
<img
src="https://garagehq.deuxfleurs.fr/images/garage-logo.svg"
src={garageLogo}
alt="logo"
className="w-full max-w-[100px] mx-auto"
/>

View File

@ -18,7 +18,6 @@ export default defineConfig(({ mode }) => {
"/api": {
target: process.env.VITE_API_URL,
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ""),
},
},
},