Compare commits
No commits in common. "main" and "new" have entirely different histories.
@ -1,19 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
if [[ ! -d "/home/ryan/Documents/Code/lifetracker" ]]; then
|
||||
echo "Cannot find source directory; Did you move it?"
|
||||
echo "(Looking for "/home/ryan/Documents/Code/lifetracker")"
|
||||
echo 'Cannot force reload with this script - use "direnv reload" manually and then try again'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# rebuild the cache forcefully
|
||||
_nix_direnv_force_reload=1 direnv exec "/home/ryan/Documents/Code/lifetracker" true
|
||||
|
||||
# Update the mtime for .envrc.
|
||||
# This will cause direnv to reload again - but without re-building.
|
||||
touch "/home/ryan/Documents/Code/lifetracker/.envrc"
|
||||
|
||||
# Also update the timestamp of whatever profile_rc we have.
|
||||
# This makes sure that we know we are up to date.
|
||||
touch -r "/home/ryan/Documents/Code/lifetracker/.envrc" "/home/ryan/Documents/Code/lifetracker/.direnv"/*.rc
|
||||
@ -1 +0,0 @@
|
||||
/nix/store/r3pr5wyvfdi4h1b3wja7jcdwqjrfk9px-source
|
||||
@ -1 +0,0 @@
|
||||
/nix/store/xpkysrb9pn0yqrm1bkll4dr2gqahj43x-source
|
||||
@ -1 +0,0 @@
|
||||
/nix/store/yj1wxm9hh8610iyzqnz75kvs6xl8j3my-source
|
||||
@ -1 +0,0 @@
|
||||
/nix/store/yp0xr7fzkk7rjgp564pn28r7f2mpdcj1-source
|
||||
@ -1 +0,0 @@
|
||||
/nix/store/11hqs52hdjhxhcb8idddn312grl1hr5i-nix-shell-env
|
||||
@ -1,7 +0,0 @@
|
||||
Dockerfile
|
||||
.dockerignore
|
||||
node_modules
|
||||
npm-debug.log
|
||||
README.md
|
||||
**/.next
|
||||
**/*.db
|
||||
38
.gitignore
vendored
@ -1,38 +0,0 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# Dependencies
|
||||
node_modules
|
||||
.pnp
|
||||
.pnp.js
|
||||
|
||||
# Local env files
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# Testing
|
||||
coverage
|
||||
|
||||
# Turbo
|
||||
.turbo
|
||||
|
||||
# Vercel
|
||||
.vercel
|
||||
|
||||
# Build Outputs
|
||||
.next/
|
||||
out/
|
||||
build
|
||||
dist
|
||||
|
||||
|
||||
# Debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
15
.sops.yaml
Normal file
@ -0,0 +1,15 @@
|
||||
# This example uses YAML anchors which allows reuse of multiple keys
|
||||
# without having to repeat yourself.
|
||||
# Also see https://github.com/Mic92/dotfiles/blob/d6114726d859df36ccaa32891c4963ae5717ef7f/nixos/.sops.yaml
|
||||
# for a more complex example.
|
||||
keys:
|
||||
- &ryan_orion age1a560amc8xx3uwku8a7tmu3spmjnfs4cvq2hr5pgnr82lwhgg5d8q892l3q
|
||||
- &ryan_thalia age1eqlkecp5w5w73y5lp79rwus76xluzmz8l62ln9m3uhpxmmcczuqs4gk47h
|
||||
- &ryan_ryanpandya age1ypva060ks8tnrhwyyu8eykgfr96d00zds3d93c8vukrrscaf3qkqscdufu
|
||||
creation_rules:
|
||||
- path_regex: secrets/[^/]+\.(yaml|json|env|ini)$
|
||||
key_groups:
|
||||
- age:
|
||||
- *ryan_orion
|
||||
- *ryan_thalia
|
||||
- *ryan_ryanpandya
|
||||
8
.vscode/settings.json
vendored
@ -1,8 +0,0 @@
|
||||
{
|
||||
"eslint.workingDirectories": [
|
||||
{
|
||||
"mode": "auto"
|
||||
}
|
||||
],
|
||||
"direnv.restart.automatic": true
|
||||
}
|
||||
63
Dockerfile
@ -1,63 +0,0 @@
|
||||
FROM node:18-alpine AS base
|
||||
|
||||
FROM base AS builder
|
||||
RUN apk update
|
||||
RUN apk add --no-cache libc6-compat postgresql
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
# Replace <your-major-version> with the major version installed in your repository. For example:
|
||||
# RUN yarn global add turbo@^2
|
||||
|
||||
RUN corepack enable
|
||||
|
||||
RUN pnpm install turbo@^2.2.3
|
||||
COPY . .
|
||||
|
||||
# Generate a partial monorepo with a pruned lockfile for a target workspace.
|
||||
# Assuming "web" is the name entered in the project's package.json: { name: "web" }
|
||||
RUN pnpm turbo prune @lifetracker/web --docker
|
||||
|
||||
# Add lockfile and package.json's of isolated subworkspace
|
||||
FROM base AS installer
|
||||
RUN apk update
|
||||
RUN apk add --no-cache libc6-compat
|
||||
WORKDIR /app
|
||||
|
||||
RUN corepack enable
|
||||
|
||||
# First install the dependencies (as they change less often)
|
||||
COPY --from=builder /app/out/json/ .
|
||||
RUN pnpm install
|
||||
|
||||
# Build the project
|
||||
COPY --from=builder /app/out/full/ .
|
||||
RUN pnpm turbo run build
|
||||
|
||||
# Add between build and runner stages:
|
||||
FROM installer AS migrator
|
||||
WORKDIR /app
|
||||
# Copy migration files
|
||||
COPY --from=installer /app/packages/db/migrations ./packages/db/migrations
|
||||
COPY --from=installer /app/packages/db/migrate.ts ./packages/db/migrate.ts
|
||||
# Run migrations
|
||||
# RUN npx packages/db/migrate.ts
|
||||
|
||||
# Then continue with the runner stage as before
|
||||
FROM base AS runner
|
||||
WORKDIR /app
|
||||
RUN corepack enable
|
||||
RUN apk add --no-cache postgresql
|
||||
|
||||
# Don't run production as root
|
||||
RUN addgroup --system --gid 1001 nodejs
|
||||
RUN adduser --system --uid 1001 nextjs
|
||||
USER nextjs
|
||||
|
||||
# Automatically leverage output traces to reduce image size
|
||||
# https://nextjs.org/docs/advanced-features/output-file-tracing
|
||||
COPY --from=installer --chown=nextjs:nodejs /app/apps/web/.next/standalone ./
|
||||
COPY --from=installer --chown=nextjs:nodejs /app/apps/web/.next/static ./apps/web/.next/static
|
||||
COPY --from=installer --chown=nextjs:nodejs /app/apps/web/public ./apps/web/public
|
||||
|
||||
EXPOSE 3000
|
||||
CMD node apps/web/server.js
|
||||
81
README.md
@ -1,81 +0,0 @@
|
||||
# Turborepo starter
|
||||
|
||||
This is an official starter Turborepo.
|
||||
|
||||
## Using this example
|
||||
|
||||
Run the following command:
|
||||
|
||||
```sh
|
||||
npx create-turbo@latest
|
||||
```
|
||||
|
||||
## What's inside?
|
||||
|
||||
This Turborepo includes the following packages/apps:
|
||||
|
||||
### Apps and Packages
|
||||
|
||||
- `docs`: a [Next.js](https://nextjs.org/) app
|
||||
- `web`: another [Next.js](https://nextjs.org/) app
|
||||
- `@lifetracker/ui`: a stub React component library shared by both `web` and `docs` applications
|
||||
- `@lifetracker/eslint-config`: `eslint` configurations (includes `eslint-config-next` and `eslint-config-prettier`)
|
||||
- `@lifetracker/typescript-config`: `tsconfig.json`s used throughout the monorepo
|
||||
|
||||
Each package/app is 100% [TypeScript](https://www.typescriptlang.org/).
|
||||
|
||||
### Utilities
|
||||
|
||||
This Turborepo has some additional tools already setup for you:
|
||||
|
||||
- [TypeScript](https://www.typescriptlang.org/) for static type checking
|
||||
- [ESLint](https://eslint.org/) for code linting
|
||||
- [Prettier](https://prettier.io) for code formatting
|
||||
|
||||
### Build
|
||||
|
||||
To build all apps and packages, run the following command:
|
||||
|
||||
```
|
||||
cd my-turborepo
|
||||
pnpm build
|
||||
```
|
||||
|
||||
### Develop
|
||||
|
||||
To develop all apps and packages, run the following command:
|
||||
|
||||
```
|
||||
cd my-turborepo
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
### Remote Caching
|
||||
|
||||
Turborepo can use a technique known as [Remote Caching](https://turbo.build/repo/docs/core-concepts/remote-caching) to share cache artifacts across machines, enabling you to share build caches with your team and CI/CD pipelines.
|
||||
|
||||
By default, Turborepo will cache locally. To enable Remote Caching you will need an account with Vercel. If you don't have an account you can [create one](https://vercel.com/signup), then enter the following commands:
|
||||
|
||||
```
|
||||
cd my-turborepo
|
||||
npx turbo login
|
||||
```
|
||||
|
||||
This will authenticate the Turborepo CLI with your [Vercel account](https://vercel.com/docs/concepts/personal-accounts/overview).
|
||||
|
||||
Next, you can link your Turborepo to your Remote Cache by running the following command from the root of your Turborepo:
|
||||
|
||||
```
|
||||
npx turbo link
|
||||
```
|
||||
|
||||
## Useful Links
|
||||
|
||||
Learn more about the power of Turborepo:
|
||||
|
||||
- [Tasks](https://turbo.build/repo/docs/core-concepts/monorepos/running-tasks)
|
||||
- [Caching](https://turbo.build/repo/docs/core-concepts/caching)
|
||||
- [Remote Caching](https://turbo.build/repo/docs/core-concepts/remote-caching)
|
||||
- [Filtering](https://turbo.build/repo/docs/core-concepts/monorepos/filtering)
|
||||
- [Configuration Options](https://turbo.build/repo/docs/reference/configuration)
|
||||
- [CLI Usage](https://turbo.build/repo/docs/reference/command-line-reference)
|
||||
@ -1,5 +0,0 @@
|
||||
SERVER_VERSION=0.1.0
|
||||
DATA_DIR=/home/ryan/Notes/lifetracker
|
||||
AUTH_SECRET=ryanisthebest
|
||||
LIFETRACKER_API_KEY=ak1_b9b17eb909ce0ecdbf33_789b3a1f59390aad84be
|
||||
LIFETRACKER_SERVER_ADDR=localhost
|
||||
0
apps/cli/.gitignore
vendored
@ -1 +0,0 @@
|
||||
# `@lifetracker/cli`
|
||||
17346
apps/cli/bin/index.js
@ -1,52 +0,0 @@
|
||||
{
|
||||
"name": "@lifetracker/cli",
|
||||
"version": "0.0.1",
|
||||
"description": "Command Line Interface (CLI) for Lifetracker",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"exports": "./dist/index.mjs",
|
||||
"bin": {
|
||||
"lifetracker": "dist/index.mjs"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "vite build --debug",
|
||||
"run": "tsx src/index.ts",
|
||||
"lint": "eslint .",
|
||||
"format": "prettier . --ignore-path ../../.prettierignore",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@date-fns/tz": "^1.2.0",
|
||||
"@date-fns/utc": "^2.1.0",
|
||||
"@lifetracker/db": "workspace:*",
|
||||
"@lifetracker/shared": "workspace:^",
|
||||
"@lifetracker/trpc": "workspace:^",
|
||||
"date-fns": "^4.1.0",
|
||||
"dotenv": "^16.4.1",
|
||||
"spacetime": "^7.6.2",
|
||||
"table": "^6.8.2",
|
||||
"timezone-soft": "^1.5.2",
|
||||
"vite-tsconfig-paths": "^5.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commander-js/extra-typings": "^12.1.0",
|
||||
"@lifetracker/db": "workspace:*",
|
||||
"@lifetracker/eslint-config": "workspace:*",
|
||||
"@lifetracker/typescript-config": "workspace:*",
|
||||
"@lifetracker/ui": "workspace:*",
|
||||
"@trpc/client": "^11.0.0-next-beta.308",
|
||||
"@trpc/server": "^11.0.0-next-beta.308",
|
||||
"chalk": "^5.3.0",
|
||||
"commander": "^12.1.0",
|
||||
"superjson": "^2.2.1",
|
||||
"tsx": "^4.19.1",
|
||||
"vite": "^5.4.10",
|
||||
"vite-tsconfig-paths": "^5.1.0"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"extends": [
|
||||
"@lifetracker/eslint-config/base"
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -1,106 +0,0 @@
|
||||
import { getGlobalOptions } from "@/lib/globals";
|
||||
import {
|
||||
printError,
|
||||
printErrorMessageWithReason,
|
||||
printObject,
|
||||
printSuccess,
|
||||
} from "@/lib/output";
|
||||
import { getAPIClient } from "@/lib/trpc";
|
||||
import { Command } from "@commander-js/extra-typings";
|
||||
import { getBorderCharacters, table } from "table";
|
||||
|
||||
export const categoriesCmd = new Command()
|
||||
.name("categories")
|
||||
.description("Manipulate categories");
|
||||
|
||||
categoriesCmd
|
||||
.command("list")
|
||||
.description("lists all defined categories")
|
||||
.action(async () => {
|
||||
const api = getAPIClient();
|
||||
|
||||
try {
|
||||
const categories = (await api.categories.list.query()).categories;
|
||||
// colors.sort((a, b) => b.numCategories - a.numCategories);
|
||||
if (getGlobalOptions().json) {
|
||||
printObject(categories);
|
||||
} else {
|
||||
const data: string[][] = [["Code", "Name", "Description", "Color"]];
|
||||
|
||||
categories.forEach((c) => {
|
||||
data.push([c.code.toString(), c.name, c.description ?? "none", c.color.name]);
|
||||
});
|
||||
console.log(
|
||||
data.length <= 1 ?
|
||||
"No categories found. Add one with `lifetracker categories create <code> <name> <description> <color>`" :
|
||||
table(data, {
|
||||
// border: getBorderCharacters("ramac"),
|
||||
// singleLine: true,
|
||||
drawVerticalLine: (lineIndex, columnCount) => {
|
||||
return lineIndex === 0 || lineIndex === columnCount;
|
||||
},
|
||||
drawHorizontalLine: (lineIndex, rowCount) => {
|
||||
return lineIndex < 2 || lineIndex === rowCount;
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
printErrorMessageWithReason("Failed to list all categories", error as object);
|
||||
}
|
||||
});
|
||||
|
||||
categoriesCmd
|
||||
.command("create")
|
||||
.description("create a category")
|
||||
.argument("<code>", "the code of the category")
|
||||
.argument("<name>", "the name of the category")
|
||||
.argument("<description>", "the description of the category")
|
||||
.argument("[color]", "the color of the category")
|
||||
.option('-p, --parent <CODE>', "specify a parent category by code")
|
||||
.action(async (codeStr: string, name: string, description: string, colorName?: string, flags?) => {
|
||||
const api = getAPIClient();
|
||||
|
||||
const color = flags?.parent === undefined ?
|
||||
(await api.colors.get.query({ colorName: colorName! }))
|
||||
:
|
||||
(await api.categories.get.query({ categoryCode: parseInt(flags.parent) })).color
|
||||
|
||||
|
||||
try {
|
||||
await api.categories.create
|
||||
.mutate({
|
||||
code: parseFloat(codeStr),
|
||||
name: name,
|
||||
description: description,
|
||||
color: color,
|
||||
})
|
||||
.then(printSuccess(`Successfully created the category "${name}"`))
|
||||
.catch(printError(`Failed to create the category "${name}"`));
|
||||
api.categories.list
|
||||
}
|
||||
catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
categoriesCmd
|
||||
.command("delete")
|
||||
.description("delete a category")
|
||||
.argument("<code>", "the code of the category")
|
||||
.action(async (codeStr: string) => {
|
||||
const api = getAPIClient();
|
||||
|
||||
try {
|
||||
await api.categories.delete
|
||||
.mutate({
|
||||
categoryCode: parseInt(codeStr),
|
||||
})
|
||||
.then(printSuccess(`Successfully deleted category "${codeStr}"`))
|
||||
.catch(printError(`Failed to delete category "${codeStr}"`));
|
||||
}
|
||||
catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
});
|
||||
@ -1,98 +0,0 @@
|
||||
import { getGlobalOptions } from "@/lib/globals";
|
||||
import {
|
||||
printError,
|
||||
printErrorMessageWithReason,
|
||||
printObject,
|
||||
printSuccess,
|
||||
} from "@/lib/output";
|
||||
import { getAPIClient } from "@/lib/trpc";
|
||||
import { Command } from "@commander-js/extra-typings";
|
||||
import { getBorderCharacters, table } from "table";
|
||||
|
||||
export const colorsCmd = new Command()
|
||||
.name("colors")
|
||||
.description("Manipulate custom colors");
|
||||
|
||||
colorsCmd
|
||||
.command("list")
|
||||
.description("lists all custom colors")
|
||||
.action(async () => {
|
||||
const api = getAPIClient();
|
||||
|
||||
try {
|
||||
const colors = (await api.colors.list.query()).colors;
|
||||
// colors.sort((a, b) => b.numCategories - a.numCategories);
|
||||
if (getGlobalOptions().json) {
|
||||
printObject(colors);
|
||||
} else {
|
||||
const data: string[][] = [["Name", "Hexcode", "Inverse"]];
|
||||
|
||||
colors.forEach((color) => {
|
||||
data.push([color.name, color.hexcode, color.inverse]);
|
||||
});
|
||||
console.log(
|
||||
data.length <= 1 ?
|
||||
"No colors found. Add one with `lifetracker colors create <name> <hexcode>`" :
|
||||
table(data, {
|
||||
// border: getBorderCharacters("ramac"),
|
||||
// singleLine: true,
|
||||
drawVerticalLine: (lineIndex, columnCount) => {
|
||||
return lineIndex === 0 || lineIndex === columnCount;
|
||||
},
|
||||
drawHorizontalLine: (lineIndex, rowCount) => {
|
||||
return lineIndex < 2 || lineIndex === rowCount;
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
printErrorMessageWithReason("Failed to list all colors", error as object);
|
||||
}
|
||||
});
|
||||
|
||||
colorsCmd
|
||||
.command("create")
|
||||
.description("create a color")
|
||||
.argument("<name>", "the name of the color")
|
||||
.argument("<hexcode>", "the hexcode of the color")
|
||||
.action(async (name: string, hexcode: string) => {
|
||||
const api = getAPIClient();
|
||||
|
||||
await api.colors.create
|
||||
.mutate({
|
||||
name: name,
|
||||
hexcode: hexcode,
|
||||
})
|
||||
.then(printSuccess(`Successfully created the color "${name}"`))
|
||||
.catch(printError(`Failed to create the color "${name}"`));
|
||||
});
|
||||
|
||||
colorsCmd
|
||||
.command("delete")
|
||||
.description("delete a color")
|
||||
.argument("<name>", "the name of the color")
|
||||
.action(async (name: string) => {
|
||||
const api = getAPIClient();
|
||||
|
||||
await api.colors.delete
|
||||
.mutate({
|
||||
colorName: name,
|
||||
})
|
||||
.then(printSuccess(`Successfully deleted the color "${name}"`))
|
||||
.catch(printError(`Failed to delete the color "${name}"`));
|
||||
});
|
||||
|
||||
// colorsCmd
|
||||
// .command("delete")
|
||||
// .description("delete a color")
|
||||
// .argument("<id>", "the id of the color")
|
||||
// .action(async (id) => {
|
||||
// const api = getAPIClient();
|
||||
|
||||
// await api.colors.delete
|
||||
// .mutate({
|
||||
// colorId: id,
|
||||
// })
|
||||
// .then(printSuccess(`Successfully deleted the color with the id "${id}"`))
|
||||
// .catch(printError(`Failed to delete the color with the id "${id}"`));
|
||||
// });
|
||||
@ -1,110 +0,0 @@
|
||||
import { getGlobalOptions } from "@/lib/globals";
|
||||
import {
|
||||
printError,
|
||||
printErrorMessageWithReason,
|
||||
printObject,
|
||||
printSuccess,
|
||||
} from "@/lib/output";
|
||||
import { getAPIClient } from "@/lib/trpc";
|
||||
import { Command } from "@commander-js/extra-typings";
|
||||
import { getBorderCharacters, table } from "table";
|
||||
import { format } from "date-fns";
|
||||
import { TZDate } from "@date-fns/tz";
|
||||
import { utc } from "@date-fns/utc";
|
||||
import { getHourFromTime, getTimeFromHour } from "@lifetracker/shared/utils/hours";
|
||||
import { timezones } from "@lifetracker/shared/utils/timezones";
|
||||
import { hours } from "@lifetracker/db/schema";
|
||||
import { doHour } from "./hours";
|
||||
import soft from "timezone-soft";
|
||||
import spacetime from "spacetime";
|
||||
|
||||
function moodToStars(mood: number) {
|
||||
// const full_stars = Math.floor(mood / 2);
|
||||
// const half_star = mood % 2;
|
||||
// return "★".repeat(full_stars) + (half_star ? "⯨" : "");
|
||||
return "★".repeat(mood) + "☆".repeat(10 - mood);
|
||||
}
|
||||
|
||||
function getTimezone(req: string) {
|
||||
try {
|
||||
const tz = soft(req)[0].iana;
|
||||
if (timezones[tz] === undefined) {
|
||||
throw new Error("Valid timezone, but not supported by Lifetracker. Add it to the timezones list in the shared utils or try a different request.");
|
||||
}
|
||||
return tz;
|
||||
} catch (e) {
|
||||
throw new Error("Invalid timezone..." + e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const daysCmd = new Command()
|
||||
.name("day")
|
||||
.description("Get data for a specific day")
|
||||
.argument('[date]', 'A date in ISO-8601 format, or "yesterday", "today", "tomorrow", etc.', "today")
|
||||
.option('-c, --comment <comment>', "edit this day's comment")
|
||||
.option('-m, --mood <number>', "edit this day's mood")
|
||||
.option('-h, --hour <hour...>', "manipulate a specific hour (<time> [code | @] [comment])")
|
||||
.option('-t, --timezone <timezone>', "display times in a specific timezone")
|
||||
.action(async (dateQuery, flags?) => {
|
||||
const api = getAPIClient();
|
||||
|
||||
try {
|
||||
const timezone = flags?.timezone
|
||||
? getTimezone(flags.timezone)
|
||||
: getTimezone(Intl.DateTimeFormat().resolvedOptions().timeZone);
|
||||
// : await api.users.getTimezone.query();
|
||||
|
||||
if (flags?.comment || flags?.mood) {
|
||||
console.log(`Updating day with date '${dateQuery}' in timezone '${flags.timezone}'`);
|
||||
const updateProps = { dateQuery: dateQuery, timezone: timezone, ...flags };
|
||||
await api.days.update.mutate(updateProps);
|
||||
}
|
||||
|
||||
console.log(`Loading ${dateQuery} in ` + timezone + ` (${spacetime.now(timezone).format('{nice}')})`);
|
||||
|
||||
const day = (await api.days.get.query({ dateQuery: dateQuery, timezone: timezone }));
|
||||
|
||||
console.log(`Snagged day with id '${day.id}'`);
|
||||
// if (getGlobalOptions().json) {
|
||||
// printObject(day);
|
||||
// } else {
|
||||
|
||||
const moodStr = moodToStars(day.mood);
|
||||
const dateStr = format(day.date, "EEEE, MMMM do", { in: utc });
|
||||
const data: string[][] = [[dateStr, '', moodStr], [day.comment ?? "", '', ''],
|
||||
[`Time (${timezones[timezone]})`, "Category", "Comment"]
|
||||
];
|
||||
|
||||
console.log(day.hours.map((h) => h.date));
|
||||
|
||||
day.hours.forEach((h, i) => {
|
||||
data.push([
|
||||
getHourFromTime(i, 'twelves', `(${timezones[timezone]})`),
|
||||
h.categoryName ?? "--",
|
||||
h.comment ?? ""
|
||||
]);
|
||||
})
|
||||
|
||||
if (flags?.hour) {
|
||||
doHour(day, flags.hour, timezone);
|
||||
}
|
||||
else {
|
||||
|
||||
console.log(table(data, {
|
||||
// border: getBorderCharacters("ramac"),
|
||||
// singleLine: true,
|
||||
spanningCells: [{ col: 0, row: 1, colSpan: 3 }, { col: 1, row: 2, colSpan: 2 }],
|
||||
drawVerticalLine: (lineIndex, columnCount) => {
|
||||
return lineIndex === 0 || lineIndex === columnCount || (lineIndex === 0 && columnCount === 2);
|
||||
},
|
||||
drawHorizontalLine: (lineIndex, rowCount) => {
|
||||
return (lineIndex < 1 || lineIndex === 2 || lineIndex === 3 || lineIndex === rowCount);
|
||||
},
|
||||
}));
|
||||
// }
|
||||
}
|
||||
} catch (error) {
|
||||
printErrorMessageWithReason("Failed", error as object);
|
||||
}
|
||||
});
|
||||
@ -1,83 +0,0 @@
|
||||
import { getGlobalOptions } from "@/lib/globals";
|
||||
import {
|
||||
printError,
|
||||
printErrorMessageWithReason,
|
||||
printObject,
|
||||
printSuccess,
|
||||
} from "@/lib/output";
|
||||
import { getAPIClient } from "@/lib/trpc";
|
||||
import { Command } from "@commander-js/extra-typings";
|
||||
import { getBorderCharacters, table } from "table";
|
||||
import { format } from "date-fns";
|
||||
import { TZDate } from "@date-fns/tz";
|
||||
import { getHourFromTime, getTimeFromHour } from "@lifetracker/shared/utils/hours";
|
||||
import { ZDay, ZHour } from "@lifetracker/shared/types/days";
|
||||
import soft from "timezone-soft";
|
||||
import spacetime from "spacetime";
|
||||
import { timezones } from "@lifetracker/shared/utils/timezones";
|
||||
|
||||
export const hoursCmd = new Command()
|
||||
.name("hour")
|
||||
.description("Get or set data for a specific hour")
|
||||
.argument('<hour>', 'An hour between 0-23, or 1-12 [AM/PM]')
|
||||
.argument('[code]', 'Optionally set the code for this hour')
|
||||
.option('-c, --comment <comment>', "edit this hour's comment")
|
||||
.action(async (dateQuery = "today", hour: string, code: string | undefined, flags?) => {
|
||||
return "Hello";
|
||||
});
|
||||
|
||||
export async function doHour(day: ZDay, hourFlags: string[], timezone = "UTC") {
|
||||
const hourNum = getTimeFromHour(hourFlags.shift()!);
|
||||
const hour = day.hours[hourNum];
|
||||
|
||||
if (hourFlags.length == 0) {
|
||||
console.log("No new values to set; printing hour.")
|
||||
printHour(hour, hourNum, day, timezone);
|
||||
}
|
||||
else {
|
||||
const api = getAPIClient();
|
||||
|
||||
let res: string | ZHour;
|
||||
try {
|
||||
if (hourFlags[0] == "@") {
|
||||
|
||||
if (!hour.categoryCode) {
|
||||
throw new Error("No category code yet -- '@' is meant to set the comment when the category is already set.");
|
||||
}
|
||||
else {
|
||||
hourFlags[0] = hour.categoryCode?.toString();
|
||||
}
|
||||
}
|
||||
const props = { dateQuery: hour.date!, time: hour.time, code: hourFlags[0], comment: hourFlags[1] || null };
|
||||
// console.log(props);
|
||||
res = await api.hours.update.mutate({ ...props });
|
||||
printHour(res as ZHour, hourNum, day, timezone);
|
||||
|
||||
}
|
||||
catch (error) {
|
||||
printErrorMessageWithReason("Failed to manipulate hour", error as object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function printHour(hour: ZHour, hourNum: number, day: ZDay, timezone: string) {
|
||||
const data = [
|
||||
[hour.categoryName + ".", hour.categoryDesc ?? "Nothing stored yet."],
|
||||
|
||||
];
|
||||
if (hour.comment) {
|
||||
data.push(['Comment:', hour.comment]);
|
||||
}
|
||||
console.log(table(data, {
|
||||
// border: getBorderCharacters("ramac"),
|
||||
// singleLine: true,
|
||||
columns: [{ width: 10 }, { width: 40 }],
|
||||
header: { alignment: "center", content: `${spacetime(day.date, timezone).format('{day}, {month-short} {date-ordinal}')} at ${getHourFromTime(hourNum, 'all', timezones[timezone])}` },
|
||||
drawVerticalLine: (lineIndex, columnCount) => {
|
||||
return lineIndex === 0 || lineIndex === columnCount || (lineIndex === 0 && columnCount === 2);
|
||||
},
|
||||
drawHorizontalLine: (lineIndex, rowCount) => {
|
||||
return (lineIndex < 2 || lineIndex === 2 || lineIndex === rowCount);
|
||||
},
|
||||
}));
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
import { getGlobalOptions } from "@/lib/globals";
|
||||
import {
|
||||
printError,
|
||||
printErrorMessageWithReason,
|
||||
printObject,
|
||||
printSuccess,
|
||||
} from "@/lib/output";
|
||||
import { getAPIClient } from "@/lib/trpc";
|
||||
import { Command } from "@commander-js/extra-typings";
|
||||
import { getBorderCharacters, table } from "table";
|
||||
|
||||
export const settingsCmd = new Command()
|
||||
.name("settings")
|
||||
.description("Manipulate user settings");
|
||||
|
||||
settingsCmd
|
||||
.command("timezone")
|
||||
.description("get or update timezone")
|
||||
.argument("[timezone]", "the timezone to set")
|
||||
.action(async (timezone?) => {
|
||||
const api = getAPIClient();
|
||||
|
||||
try {
|
||||
if (timezone) {
|
||||
const res = await api.users.changeTimezone.mutate({ newTimezone: timezone });
|
||||
console.log(`Updated to ${res}.`)
|
||||
return;
|
||||
}
|
||||
else {
|
||||
const res = await api.users.getTimezone.query();
|
||||
console.log(`Current timezone is ${res}.`);
|
||||
}
|
||||
} catch (error) {
|
||||
printErrorMessageWithReason("Failed to do the thing", error as object);
|
||||
}
|
||||
});
|
||||
@ -1,21 +0,0 @@
|
||||
import {
|
||||
printError,
|
||||
printErrorMessageWithReason,
|
||||
printObject,
|
||||
} from "@/lib/output";
|
||||
import { getAPIClient } from "@/lib/trpc";
|
||||
import { Command } from "@commander-js/extra-typings";
|
||||
|
||||
export const whoamiCmd = new Command()
|
||||
.name("whoami")
|
||||
.description("returns info about the owner of this API key")
|
||||
.action(async () => {
|
||||
await getAPIClient()
|
||||
.users.whoami.query()
|
||||
.then(printObject)
|
||||
.catch(
|
||||
printError(
|
||||
`Unable to fetch information about the owner of this API key`,
|
||||
),
|
||||
);
|
||||
});
|
||||
@ -1,51 +0,0 @@
|
||||
import { setGlobalOptions } from "@/lib/globals";
|
||||
import { Command, Option } from "@commander-js/extra-typings";
|
||||
|
||||
import { whoamiCmd } from "@/commands/whoami";
|
||||
import { colorsCmd } from "@/commands/colors";
|
||||
import { categoriesCmd } from "@/commands/categories";
|
||||
import { daysCmd } from "@/commands/days";
|
||||
import { hoursCmd } from "./commands/hours";
|
||||
import { settingsCmd } from "./commands/settings";
|
||||
|
||||
import { config } from "dotenv";
|
||||
|
||||
config({
|
||||
path: "./.env.local",
|
||||
}
|
||||
)
|
||||
|
||||
const program = new Command()
|
||||
.name("lifetracker")
|
||||
.description("A CLI interface to interact with my lifetracker api")
|
||||
.addOption(
|
||||
new Option("--api-key <key>", "the API key to interact with the API")
|
||||
.makeOptionMandatory(true)
|
||||
.env("LIFETRACKER_API_KEY"),
|
||||
)
|
||||
.addOption(
|
||||
new Option(
|
||||
"--server-addr <addr>",
|
||||
"the address of the server to connect to",
|
||||
)
|
||||
.makeOptionMandatory(true)
|
||||
.env("LIFETRACKER_SERVER_ADDR"),
|
||||
)
|
||||
.addOption(new Option("--json", "to output the result as JSON"))
|
||||
.version(
|
||||
import.meta.env && "CLI_VERSION" in import.meta.env
|
||||
? import.meta.env.CLI_VERSION
|
||||
: "0.0.0",
|
||||
);
|
||||
|
||||
program.addCommand(whoamiCmd);
|
||||
program.addCommand(daysCmd);
|
||||
program.addCommand(colorsCmd);
|
||||
program.addCommand(categoriesCmd);
|
||||
program.addCommand(hoursCmd);
|
||||
program.addCommand(settingsCmd);
|
||||
|
||||
|
||||
setGlobalOptions(program.opts());
|
||||
|
||||
program.parse();
|
||||
@ -1,18 +0,0 @@
|
||||
export interface GlobalOptions {
|
||||
apiKey: string;
|
||||
serverAddr: string;
|
||||
json?: false;
|
||||
}
|
||||
|
||||
export let globalOpts: GlobalOptions | undefined = undefined;
|
||||
|
||||
export function setGlobalOptions(opts: GlobalOptions) {
|
||||
globalOpts = opts;
|
||||
}
|
||||
|
||||
export function getGlobalOptions() {
|
||||
if (!globalOpts) {
|
||||
throw new Error("Global options are not initalized yet");
|
||||
}
|
||||
return globalOpts;
|
||||
}
|
||||
@ -1,61 +0,0 @@
|
||||
import { InspectOptions } from "util";
|
||||
import chalk from "chalk";
|
||||
|
||||
import { getGlobalOptions } from "./globals";
|
||||
|
||||
/**
|
||||
* Prints an object either in a nicely formatted way or as JSON (depending on the command flag --json)
|
||||
*
|
||||
* @param output
|
||||
*/
|
||||
export function printObject(
|
||||
output: object,
|
||||
extraOptions?: InspectOptions,
|
||||
): void {
|
||||
if (getGlobalOptions().json) {
|
||||
console.log(JSON.stringify(output, undefined, 4));
|
||||
} else {
|
||||
console.dir(output, extraOptions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to output a status (success/error) and a message either as string or as JSON (depending on the command flag --json)
|
||||
*
|
||||
* @param success if the message is a successful message or an error
|
||||
* @param output the message to output
|
||||
*/
|
||||
export function printStatusMessage(success: boolean, message: unknown): void {
|
||||
const status = success ? "Success" : "Error";
|
||||
const colorFunction = success ? chalk.green : chalk.red;
|
||||
console.error(colorFunction(`${status}: ${message}`));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param message The message that will be printed as a successful message
|
||||
* @returns a function that can be used in a Promise on success
|
||||
*/
|
||||
export function printSuccess(message: string) {
|
||||
return () => {
|
||||
printStatusMessage(true, message);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param message The message that will be printed as an error message
|
||||
* @returns a function that can be used in a Promise on rejection
|
||||
*/
|
||||
export function printError(message: string) {
|
||||
return (error: object) => {
|
||||
printErrorMessageWithReason(message, error);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param message The message that will be printed as an error message
|
||||
* @param error an error object with the reason for the error
|
||||
*/
|
||||
export function printErrorMessageWithReason(message: string, error: object) {
|
||||
const errorMessage = "message" in error ? error.message : error;
|
||||
printStatusMessage(false, `${message}. Reason: ${errorMessage}`);
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
import { getGlobalOptions } from "@/lib/globals";
|
||||
import { createTRPCProxyClient, httpBatchLink } from "@trpc/client";
|
||||
import superjson from "superjson";
|
||||
import type { AppRouter } from "@lifetracker/trpc/routers/_app";
|
||||
|
||||
export function getAPIClient() {
|
||||
const globals = getGlobalOptions();
|
||||
|
||||
return createTRPCProxyClient<AppRouter>({
|
||||
links: [
|
||||
httpBatchLink({
|
||||
transformer: superjson,
|
||||
url: `${globals.serverAddr}/api/trpc`,
|
||||
maxURLLength: 14000,
|
||||
headers() {
|
||||
return {
|
||||
authorization: `Bearer ${globals.apiKey}`,
|
||||
};
|
||||
},
|
||||
},),
|
||||
],
|
||||
});
|
||||
}
|
||||
9
apps/cli/src/vite-env.d.ts
vendored
@ -1,9 +0,0 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly CLI_VERSION: string;
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv;
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@lifetracker/typescript-config/node.json",
|
||||
"include": [
|
||||
"src/**/*",
|
||||
"vite.config.mts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json",
|
||||
"strictNullChecks": true,
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
]
|
||||
},
|
||||
"types": [
|
||||
"vite/client"
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
// Hilariously, THIS is stolen from Hoarder's CLI vite config
|
||||
// https://github.com/hoarder-app/hoarder/blob/main/apps/cli/vite.config.mts
|
||||
// This file is shamelessly copied from immich's CLI vite config
|
||||
// https://github.com/immich-app/immich/blob/main/cli/vite.config.ts
|
||||
|
||||
import { defineConfig } from "vite";
|
||||
import tsconfigPaths from "vite-tsconfig-paths";
|
||||
|
||||
export default defineConfig({
|
||||
build: {
|
||||
rollupOptions: {
|
||||
input: "src/index.ts",
|
||||
output: {
|
||||
dir: "bin",
|
||||
},
|
||||
},
|
||||
ssr: true,
|
||||
},
|
||||
ssr: {
|
||||
// bundle everything except for Node built-ins
|
||||
noExternal: /^(?!node:).*$/,
|
||||
},
|
||||
plugins: [tsconfigPaths()],
|
||||
define: {
|
||||
"import.meta.env.CLI_VERSION": JSON.stringify(
|
||||
process.env.npm_package_version,
|
||||
),
|
||||
},
|
||||
});
|
||||
20
apps/docs/.gitignore
vendored
@ -1,20 +0,0 @@
|
||||
# Dependencies
|
||||
/node_modules
|
||||
|
||||
# Production
|
||||
/build
|
||||
|
||||
# Generated files
|
||||
.docusaurus
|
||||
.cache-loader
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
@ -1,41 +0,0 @@
|
||||
# Website
|
||||
|
||||
This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator.
|
||||
|
||||
### Installation
|
||||
|
||||
```
|
||||
$ yarn
|
||||
```
|
||||
|
||||
### Local Development
|
||||
|
||||
```
|
||||
$ yarn start
|
||||
```
|
||||
|
||||
This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
|
||||
|
||||
### Build
|
||||
|
||||
```
|
||||
$ yarn build
|
||||
```
|
||||
|
||||
This command generates static content into the `build` directory and can be served using any static contents hosting service.
|
||||
|
||||
### Deployment
|
||||
|
||||
Using SSH:
|
||||
|
||||
```
|
||||
$ USE_SSH=true yarn deploy
|
||||
```
|
||||
|
||||
Not using SSH:
|
||||
|
||||
```
|
||||
$ GIT_USER=<Your GitHub username> yarn deploy
|
||||
```
|
||||
|
||||
If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
|
||||
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
|
||||
};
|
||||
@ -1,12 +0,0 @@
|
||||
---
|
||||
slug: first-blog-post
|
||||
title: First Blog Post
|
||||
authors: [slorber, yangshun]
|
||||
tags: [hola, docusaurus]
|
||||
---
|
||||
|
||||
Lorem ipsum dolor sit amet...
|
||||
|
||||
<!-- truncate -->
|
||||
|
||||
...consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
@ -1,44 +0,0 @@
|
||||
---
|
||||
slug: long-blog-post
|
||||
title: Long Blog Post
|
||||
authors: yangshun
|
||||
tags: [hello, docusaurus]
|
||||
---
|
||||
|
||||
This is the summary of a very long blog post,
|
||||
|
||||
Use a `<!--` `truncate` `-->` comment to limit blog post size in the list view.
|
||||
|
||||
<!-- truncate -->
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
@ -1,24 +0,0 @@
|
||||
---
|
||||
slug: mdx-blog-post
|
||||
title: MDX Blog Post
|
||||
authors: [slorber]
|
||||
tags: [docusaurus]
|
||||
---
|
||||
|
||||
Blog posts support [Docusaurus Markdown features](https://docusaurus.io/docs/markdown-features), such as [MDX](https://mdxjs.com/).
|
||||
|
||||
:::tip
|
||||
|
||||
Use the power of React to create interactive blog posts.
|
||||
|
||||
:::
|
||||
|
||||
{/* truncate */}
|
||||
|
||||
For example, use JSX to create an interactive button:
|
||||
|
||||
```js
|
||||
<button onClick={() => alert('button clicked!')}>Click me!</button>
|
||||
```
|
||||
|
||||
<button onClick={() => alert('button clicked!')}>Click me!</button>
|
||||
|
Before Width: | Height: | Size: 94 KiB |
@ -1,29 +0,0 @@
|
||||
---
|
||||
slug: welcome
|
||||
title: Welcome
|
||||
authors: [slorber, yangshun]
|
||||
tags: [facebook, hello, docusaurus]
|
||||
---
|
||||
|
||||
[Docusaurus blogging features](https://docusaurus.io/docs/blog) are powered by the [blog plugin](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog).
|
||||
|
||||
Here are a few tips you might find useful.
|
||||
|
||||
<!-- truncate -->
|
||||
|
||||
Simply add Markdown files (or folders) to the `blog` directory.
|
||||
|
||||
Regular blog authors can be added to `authors.yml`.
|
||||
|
||||
The blog post date can be extracted from filenames, such as:
|
||||
|
||||
- `2019-05-30-welcome.md`
|
||||
- `2019-05-30-welcome/index.md`
|
||||
|
||||
A blog post folder can be convenient to co-locate blog post images:
|
||||
|
||||

|
||||
|
||||
The blog supports tags as well!
|
||||
|
||||
**And if you don't want a blog**: just delete this directory, and use `blog: false` in your Docusaurus config.
|
||||
@ -1,23 +0,0 @@
|
||||
yangshun:
|
||||
name: Yangshun Tay
|
||||
title: Front End Engineer @ Facebook
|
||||
url: https://github.com/yangshun
|
||||
image_url: https://github.com/yangshun.png
|
||||
page: true
|
||||
socials:
|
||||
x: yangshunz
|
||||
github: yangshun
|
||||
|
||||
slorber:
|
||||
name: Sébastien Lorber
|
||||
title: Docusaurus maintainer
|
||||
url: https://sebastienlorber.com
|
||||
image_url: https://github.com/slorber.png
|
||||
page:
|
||||
# customize the url of the author page at /blog/authors/<permalink>
|
||||
permalink: '/all-sebastien-lorber-articles'
|
||||
socials:
|
||||
x: sebastienlorber
|
||||
linkedin: sebastienlorber
|
||||
github: slorber
|
||||
newsletter: https://thisweekinreact.com
|
||||
@ -1,19 +0,0 @@
|
||||
facebook:
|
||||
label: Facebook
|
||||
permalink: /facebook
|
||||
description: Facebook tag description
|
||||
|
||||
hello:
|
||||
label: Hello
|
||||
permalink: /hello
|
||||
description: Hello tag description
|
||||
|
||||
docusaurus:
|
||||
label: Docusaurus
|
||||
permalink: /docusaurus
|
||||
description: Docusaurus tag description
|
||||
|
||||
hola:
|
||||
label: Hola
|
||||
permalink: /hola
|
||||
description: Hola tag description
|
||||
@ -1,88 +0,0 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
description: Turn an empty folder into something magical.
|
||||
---
|
||||
|
||||
# First Steps
|
||||
|
||||
:::tip
|
||||
|
||||
If you fuck around with documentation, you don't have to do any hard stuff.
|
||||
|
||||
:::
|
||||
|
||||
## Setting up the repo
|
||||
|
||||
### Create a project
|
||||
|
||||
Start by running `init-nix raw lifetracker`. This uses my `init-nix` script to initialize a folder in `~/Documents/Code/lifetracker` containing a `.envrc` file and a bare-bones `flake.nix`.
|
||||
|
||||
For posterity (aka, to test Docusaurus code highlighting, since this is very much IN the repo), here's the flake.nix with key lines highlighted:
|
||||
|
||||
```nix title="~/Documents/Code/lifetracker/flake.nix"
|
||||
{
|
||||
description = "Lifetracker Monorepo";
|
||||
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
inputs.flake-utils.url = "github:numtide/flake-utils";
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils }:
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
inherit (pkgs.lib) optional optionals;
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
|
||||
|
||||
in
|
||||
with pkgs;
|
||||
{
|
||||
devShell = pkgs.mkShell {
|
||||
buildInputs = [
|
||||
bashInteractive
|
||||
glibcLocales
|
||||
git
|
||||
emacs-nox
|
||||
// highlight-start
|
||||
nodejs
|
||||
pnpm
|
||||
// highlight-end
|
||||
] ++ optional stdenv.isLinux inotify-tools
|
||||
++ optional stdenv.isDarwin terminal-notifier
|
||||
++ optionals stdenv.isDarwin (with darwin.apple_sdk.frameworks; [
|
||||
CoreFoundation
|
||||
CoreServices
|
||||
]);
|
||||
};
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Monorepo with pnpm and turbo
|
||||
|
||||
In the new directory, run `npx create turbo@latest`. If you're in the future trying this, yes, you'll realize it wants to make a subdirectory and complains about existing files, so you'll have to end up deleting everything, using the npx script, and then re-initializing the nix flake directory. Annoying, but manageable.
|
||||
|
||||
At this point, you have a pretty decent starting point. Looking at the files, you'll see references to `@lifetracker` that will need to be changed. Also, nothing will work and you'll get annoying messages until you run `pnpm install`. You'd think the error messages would hint at this, but you'd be dead wrong.
|
||||
|
||||
:::warning
|
||||
|
||||
I just made this point, but look, a warning box telling you to run `pnpm install`!
|
||||
|
||||
:::
|
||||
|
||||
## Baby's First Steps
|
||||
|
||||
This is the part where I ripped out the Next.js guts from the `docs` folder and replaced it with Docusaurus. I gotta admit, it's an incredibly satisfying static site generator.
|
||||
|
||||
Am I now wasting my time writing documentation and pretending it's productive? Sure. But, you know, good documentation is a gift, they say.
|
||||
|
||||
### Adding Docusaurus
|
||||
|
||||
So, how do you actually add Docusaurus to a fresh newly-generated Turbo app? Of course, this isn't the most straightforward either. I renamed `docs` to `docs.old`, renamed its `package.json` to `package.json.old` (so that the `pnpm-workspace.yaml` doesn't try to load shit from the old docs folder), then ran
|
||||
|
||||
```
|
||||
npx create-docusaurus@latest docs classic
|
||||
```
|
||||
|
||||
from the `apps` directory. This created a new `apps/docs` subdirectory with a Docusaurus, uh... install, I guess.
|
||||
|
||||
After another `pnpm install`, and then editing the name, version, dependencies, and scripts (I just renamed "start" to "dev" so that `pnpm run dev` starts Docusaurus) --- we're good to go!
|
||||
@ -1,32 +0,0 @@
|
||||
---
|
||||
description: Time to actually do shit.
|
||||
---
|
||||
|
||||
# Repo Hygiene
|
||||
|
||||
So you got a project started and wasted some time writing documentation. Great!
|
||||
|
||||
Now, let's move on to "actually building software that does something" and "everything else other than running a generator and writing useless documentation".
|
||||
|
||||
## But first, wtf is @repo?
|
||||
|
||||
Let's change all the auto-generated `@repo` references to `@lifetracker`. To do this, I am about to use VSCode's Find and Replace feature.
|
||||
|
||||
Yeah, I know, mind blowing stuff so far.
|
||||
|
||||
But, keep in mind, I have Typescript server running and I'm able to load `localhost:3000` and `localhost:3001` without errors. Will everything break as soon as I make these changes?
|
||||
|
||||
:::info
|
||||
|
||||
Yes, everything worked fine as soon as I made the ~31 edits, ran `pnpm install`, restarted the Typescript server, and re-ran `pnpm dev`.
|
||||
|
||||
:::
|
||||
|
||||
## Another side-quest: Git Repository
|
||||
|
||||
There's really no reason to document this. I already had a local a git repo initialized (can't remember if this happens by default in my `init-nix` script, or if I ran `git init`, you're on your own); I created a cloud copy on `git.ryanpandya.com` and synced all this crap with it.
|
||||
|
||||
:::note
|
||||
|
||||
Make sure you de-select "Initialize repository" so that the first commit from VSCode actually goes through instead of causing a mountain of sync conflicts and merge headaches.
|
||||
:::
|
||||
@ -1,53 +0,0 @@
|
||||
---
|
||||
description: The scary stuff.
|
||||
---
|
||||
|
||||
# Database
|
||||
|
||||
The scary part.
|
||||
|
||||
This is where my casual fucking around is about to slam face-first into a brick wall.
|
||||
|
||||
It's time to:
|
||||
|
||||
- Create a database package.
|
||||
- ~Connect to a postgres database.~ JK, Fuck postgres, I'll use SQLite to keep things easy for now.
|
||||
|
||||
I'll worry about formalizing these things later (like, how can I configure these through environment variables, make things multi-user... etc. Not my problem right now. This is single-user and mostly for learning).
|
||||
|
||||
## Setting Up Drizzle
|
||||
|
||||
Continuing my copy-catting from Hoarder, it looks like Drizzle is the thing to use for database schema type shit in Javascript/Typescript/Node land.
|
||||
|
||||
Let's see how that works.
|
||||
|
||||
### Create folder
|
||||
|
||||
`mkdir packages/db`.
|
||||
|
||||
`pnpm init`.
|
||||
|
||||
Shamelessly copy code from Hoarder.
|
||||
|
||||
### Set up SQLite with Drizzle
|
||||
|
||||
```
|
||||
pnpm add -D drizzle-kit
|
||||
```
|
||||
|
||||
Create `drizzle.config.ts`.
|
||||
|
||||
Honestly. just copy the code from the following files:
|
||||
|
||||
- drizzle.config.ts
|
||||
- drizzle.ts
|
||||
- index.ts
|
||||
- migrate.ts
|
||||
- package.json
|
||||
- schema.ts (PLACEHOLDER SHIT FROM TUTORIAL)
|
||||
|
||||
Then, _from the db directory_, run `pnpm drizzle-kit generate` and it'll make the fucking database file, after which, from the workspace project root, `pnpm db:migrate` will do the goddamn migration, after which, `pnpm db:studio` will load Drizzle Studio at some arcane Avahi `local.drizzle.studio` URL and you'll see the fucking table it made.
|
||||
|
||||
I hate Javascript development.
|
||||
|
||||
But, it's starting to work, goddamn it.
|
||||
@ -1,108 +0,0 @@
|
||||
---
|
||||
description: Time for some actual functionality, believe it or not.
|
||||
---
|
||||
|
||||
# Baby's First Schema
|
||||
|
||||
Unsurprisingly, this takes place in the database part of the project, which is `packages/db`.
|
||||
|
||||
The idea will be to define a new file, `day.ts`, in `packages/db/schema/`, and put the columns and constraints in there.
|
||||
|
||||
If I'm understanding correctly, a difference vs. Phoenix or Rails is that you don't have to write the migrations yourself: just edit the schema file and you can `drizzle-kit generate` the migrations straight out of the single file. Plus, it'll walk you through which columns you want to rename, which you want to make new, etc. Slick.
|
||||
|
||||
## But first, a detour...
|
||||
|
||||
I guess last time I had set up a schema in `packages/db/schema.ts`, and the migrations were in `packages/db/drizzle` -- I felt it was better to rename these:
|
||||
|
||||
- Schemas in their own directory (`schema`)
|
||||
- Migrations called `migrations`, not `drizzle`
|
||||
|
||||
These changes required editing the front matter of a few Drizzle ts files, which I'm sure you can figure out for yourself.
|
||||
|
||||
Then I edited `day.ts` and prepared to generate and run the migrations.
|
||||
|
||||
This was the harder part that I do want to document:
|
||||
|
||||
### 1. Why won't the db generate command work
|
||||
|
||||
After changing the "events" schema I had started with last time to a "days" schema -- mainly to see if the db could migrate from one schema to another conflicting one -- I wasn't able to run the generate command:
|
||||
|
||||
```
|
||||
pnpm drizzle-kit generate
|
||||
```
|
||||
|
||||
`drizzle-kit` wasn't in the PATH, which was actually irrelevant, because I wanted to invoke this using `pnpm db:generate` the way I was able to run `pnpm db:migrate` and `pnpm db:studio`. Why wasn't it working?
|
||||
|
||||
The answer involved two steps. First, I had to add in `packages/db/package.json` a script:
|
||||
|
||||
```js
|
||||
"scripts": {
|
||||
"generate": "drizzle-kit generate"
|
||||
}
|
||||
```
|
||||
|
||||
which was obvious. What was NOT obvious was that the `db:___` syntax wasn't filtering into the db package for me, it was just already defined for `migrate` and `studio` in the root level package.json. Added `db:generate` in there and everything worked great.
|
||||
|
||||
```js
|
||||
"scripts": {
|
||||
"build": "turbo build",
|
||||
"dev": "turbo dev",
|
||||
...
|
||||
"db:generate": "pnpm --filter @lifetracker/db generate",
|
||||
```
|
||||
|
||||
Now, why not have this somehow alias to the one in the `packages/db/package.json` file? IDK, but the migrate and studio ones were written this way, so fuck it.
|
||||
|
||||
### 2. Adding "db:studio" to the dev pipeline in Turbo
|
||||
|
||||
With the migration ostensibly done, I wanted to see the glorious new table definition in the Drizzle Studio, but despite having run `pnpm run dev`, it wasn't running. How could I get it to run alongside?
|
||||
|
||||
This was pretty cool. I changed this in `turbo.json` at the project root:
|
||||
|
||||
```js
|
||||
"dev": {
|
||||
"dependsOn": ["^dev"], # This is the key
|
||||
"cache": false,
|
||||
"persistent": true
|
||||
}
|
||||
```
|
||||
|
||||
... and in the db package's `package.json`, I added:
|
||||
|
||||
```js
|
||||
"scripts": {
|
||||
...
|
||||
"dev": "drizzle-kit studio",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
This means that now when I run "pnpm dev" from the project root, along with Docusaurus (this shit) and the actual web app, I get the Drizzle Studio page simultaneously. So cool!
|
||||
|
||||
## Can we actually do something now?
|
||||
|
||||
Almost! Next snag: Trying to add `@lifetracker/db` as a dependency in the `@lifetracker/web` app, I realize I could just manually add it in the package.json, but ChatGPT suggested I run this command:
|
||||
|
||||
```bash
|
||||
$ pnpm --filter=@lifetracker/web add @lifetracker/db
|
||||
|
||||
ERR_PNPM_FETCH_404 GET https://registry.npmjs.org/@lifetracker%2Fdb: Not Found - 404
|
||||
|
||||
This error happened while installing a direct dependency of /home/ryan/Documents/Code/lifetracker/apps/web
|
||||
|
||||
@lifetracker/db is not in the npm registry, or you have no permission to fetch it.
|
||||
```
|
||||
|
||||
So, to fix this... you actually have to run
|
||||
|
||||
```
|
||||
pnpm --filter=@lifetracker/web add @lifetracker/db@workspace:*
|
||||
```
|
||||
|
||||
which is also why in the manually defined lines I had copied over into the package.json, it was written as:
|
||||
|
||||
```js
|
||||
"@lifetracker/db": "workspace:*^*",
|
||||
```
|
||||
|
||||
This made it work fine.
|
||||
@ -1,142 +0,0 @@
|
||||
---
|
||||
description: A quick and dirty command-line interface would be cool to set up.
|
||||
---
|
||||
|
||||
# Command-Line App
|
||||
|
||||
In the main project root, run:
|
||||
|
||||
```
|
||||
pnpm turbo generate workspace --name "@lifetracker/cli" --type app
|
||||
```
|
||||
|
||||
I let the wizard walk me through it, selecting only the `@lifetracker/db` as regular dependencies, and all four (db, ui, eslint-config, and typescript-config) as devDependencies --- no idea if that was right.
|
||||
|
||||
I also added a few more devDependencies (`-D`):
|
||||
|
||||
```
|
||||
pnpm --filter=@lifetracker/cli add -D tsx vite commander @commander-js/extra-typings chalk @trpc/client @trpc/server
|
||||
```
|
||||
|
||||
As far as I can tell, Commander parses command line args, Chalk is for adding terminal colors and such, TRPC is the actual communication with the API (?), TSX is Typescript, and Vite is the build system (because apparently, pnpm and turbo aren't enough Javascript crust for one project).
|
||||
|
||||
Don't forget the scripts:
|
||||
|
||||
```js
|
||||
"scripts": {
|
||||
"build": "vite build",
|
||||
"run": "tsx src/index.ts",
|
||||
"lint": "eslint .",
|
||||
"format": "prettier . --ignore-path ../../.prettierignore",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
```
|
||||
|
||||
and exports:
|
||||
|
||||
```js
|
||||
"exports": "./dist/index.mjs",
|
||||
"bin": {
|
||||
"lifetracker": "dist/index.mjs"
|
||||
},
|
||||
```
|
||||
|
||||
I then copied Hoarder's `tsconfig.json`, `.gitignore`, `vite.config.mts`, and in the `src/` folder, `vite-env.d.ts`, the files in `lib` which mostly didn't need changing, and then I whipped up a Hello World command definition.
|
||||
|
||||
There were now dependency issues because I don't actually have `@lifetracker/trpc` yet, and there was a missing typescript definition for node files, which I quickly stole from Hoarder.
|
||||
|
||||
Next snag before I write some TRPC code is that `vite-tsconfig-paths`, which is referenced a bunch in Hoarder but defined/imported nowhere it seems, is... not imported for me. What is this thing?
|
||||
|
||||
Looking at Hoarder's repo, it seems `vite-tsconfig-paths` is in the trpc and web `package.json` files, but not in the CLI one. So, I added it and it works.
|
||||
|
||||
## TRPC Time
|
||||
|
||||
Now to make the actual TRPC interface to interface with the CLI app.
|
||||
|
||||
First, before worrying about what in Zombie Jesus "TRPC" is, let's mindlessly create a new package for it:
|
||||
|
||||
```
|
||||
pnpm turbo generate workspace --name "@lifetracker/trpc" --type package
|
||||
```
|
||||
|
||||
and shamelessly copy the devDependencies:
|
||||
|
||||
```
|
||||
pnpm --filter=@lifetracker/trpc add -D vitest vite-tsconfig-paths @types/bcryptjs
|
||||
```
|
||||
|
||||
:::info
|
||||
This time we're not fooled by `vite-tsconfig-paths`.
|
||||
:::
|
||||
|
||||
There are also a few non-dev dependencies we need (I'm guessing, since they're in the project I'm stealing from):
|
||||
|
||||
```
|
||||
pnpm --filter=@lifetracker/trpc add @trpc/server bcryptjs drizzle-orm superjson tiny-invariant zod
|
||||
```
|
||||
|
||||
Of these, the two non-obvious ones are tiny-invariant (a stupid little microrepo that converts truthy and falsy values to thrown errors or not) and zod (TypeScript-first schema validation with static type inference[^1]).
|
||||
|
||||
[^1]: What the fuck does that mean? Sounds like something that should have been part of Drizzle?
|
||||
|
||||
... and shamelessly copy the main files in Hoarder's trpc package:
|
||||
|
||||
- `vitest.config.ts`
|
||||
- `tsconfig.json`
|
||||
- `testUtils.ts`, which needs a lot of work to modify for us
|
||||
- `index.ts`, same deal plus we might need to make a Shared package
|
||||
- I'm gonna skip `auth.ts` for now, I don't need it for the hello world CLI function
|
||||
|
||||
The real magic happens in `trpc/routers` so let's steal that next.
|
||||
|
||||
### Routers?
|
||||
|
||||
Seeing as the only thing directly referenced in the CLI so far is `_app`, let's just do that and see if something can limp towards working from there:
|
||||
|
||||
Okay yeah so `_app.ts` is super simple, it's just a directory of the different modules in the folder. But each of those modules is a doozie to copy and simplify. Let me give it a go.
|
||||
|
||||
### Bare Minimum TRPC
|
||||
|
||||
All `index.ts` needs is this:
|
||||
|
||||
```js
|
||||
import { initTRPC } from "@trpc/server";
|
||||
|
||||
const t = initTRPC.create();
|
||||
export const router = t.router;
|
||||
export const publicProcedure = t.procedure;
|
||||
```
|
||||
|
||||
All the router `helloWorld.js` needs is:
|
||||
|
||||
```js
|
||||
import { printError, printErrorMessageWithReason, printObject } from "@/lib/output";
|
||||
import { getAPIClient } from "@/lib/trpc";
|
||||
import { Command } from "@commander-js/extra-typings";
|
||||
|
||||
export const helloWorldCmd = new Command()
|
||||
.name("hello-world")
|
||||
.description("returns info about the server and stats");
|
||||
|
||||
helloWorldCmd
|
||||
.command("test")
|
||||
.description("does something specific I guess")
|
||||
.action(async () => {
|
||||
const api = getAPIClient();
|
||||
try {
|
||||
console.dir(api);
|
||||
} catch (error) {
|
||||
printErrorMessageWithReason("Something went horribly wrong", error as object);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Time to test, and of course `pnpm build` fails spectacularly, and ChatGPT and Google results are zero help. It seems like the plugin `vite-tsconfig-paths` (YEAH, THAT ONE) is not fucking working in the build step, so it can't resolve paths that are resolved just fine by the TS server running in VSCode.
|
||||
|
||||
After literally hours of nothing useful on Google and complete trash from the AI, I figured it out all on my own: the issue was the first line in the file,
|
||||
|
||||
```
|
||||
#!/usr/bin/env node
|
||||
```
|
||||
|
||||
Removing it works fine, but then how will
|
||||
@ -1,7 +0,0 @@
|
||||
label: 'How I Built This'
|
||||
collapsible: true # make the category collapsible
|
||||
collapsed: false # keep the category open by default
|
||||
link:
|
||||
type: generated-index
|
||||
title: How I Built This
|
||||
description: There's no fucking way I'm going to remember all this arcane Javascript bullshittery, so I'm documenting it here.
|
||||
@ -1,142 +0,0 @@
|
||||
import { themes as prismThemes } from "prism-react-renderer";
|
||||
import type { Config } from "@docusaurus/types";
|
||||
import type * as Preset from "@docusaurus/preset-classic";
|
||||
|
||||
const config: Config = {
|
||||
title: "Lifetracker Docs",
|
||||
tagline: "Tracking the life of the life tracker",
|
||||
favicon: "img/favicon.ico",
|
||||
|
||||
// Set the production url of your site here
|
||||
url: "https://your-docusaurus-site.example.com",
|
||||
// Set the /<baseUrl>/ pathname under which your site is served
|
||||
// For GitHub pages deployment, it is often '/<projectName>/'
|
||||
baseUrl: "/",
|
||||
|
||||
// GitHub pages deployment config.
|
||||
// If you aren't using GitHub pages, you don't need these.
|
||||
organizationName: "facebook", // Usually your GitHub org/user name.
|
||||
projectName: "docusaurus", // Usually your repo name.
|
||||
|
||||
onBrokenLinks: "throw",
|
||||
onBrokenMarkdownLinks: "warn",
|
||||
|
||||
// Even if you don't use internationalization, you can use this field to set
|
||||
// useful metadata like html lang. For example, if your site is Chinese, you
|
||||
// may want to replace "en" with "zh-Hans".
|
||||
i18n: {
|
||||
defaultLocale: "en",
|
||||
locales: ["en"],
|
||||
},
|
||||
|
||||
presets: [
|
||||
[
|
||||
"classic",
|
||||
{
|
||||
docs: {
|
||||
sidebarPath: "./sidebars.ts",
|
||||
// Please change this to your repo.
|
||||
// Remove this to remove the "edit this page" links.
|
||||
editUrl:
|
||||
"https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/",
|
||||
},
|
||||
blog: {
|
||||
showReadingTime: true,
|
||||
feedOptions: {
|
||||
type: ["rss", "atom"],
|
||||
xslt: true,
|
||||
},
|
||||
// Please change this to your repo.
|
||||
// Remove this to remove the "edit this page" links.
|
||||
editUrl:
|
||||
"https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/",
|
||||
// Useful options to enforce blogging best practices
|
||||
onInlineTags: "warn",
|
||||
onInlineAuthors: "warn",
|
||||
onUntruncatedBlogPosts: "warn",
|
||||
},
|
||||
theme: {
|
||||
customCss: "./src/css/custom.css",
|
||||
},
|
||||
} satisfies Preset.Options,
|
||||
],
|
||||
],
|
||||
|
||||
themeConfig: {
|
||||
// Replace with your project's social card
|
||||
image: "img/docusaurus-social-card.jpg",
|
||||
navbar: {
|
||||
title: "Lifetracker Docs",
|
||||
logo: {
|
||||
alt: "My Site Logo",
|
||||
src: "img/logo.svg",
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: "docSidebar",
|
||||
sidebarId: "tutorialSidebar",
|
||||
position: "left",
|
||||
label: "Docs",
|
||||
},
|
||||
{ to: "/blog", label: "Blog", position: "left" },
|
||||
{
|
||||
href: "https://github.com/facebook/docusaurus",
|
||||
label: "GitHub",
|
||||
position: "right",
|
||||
},
|
||||
],
|
||||
},
|
||||
footer: {
|
||||
style: "dark",
|
||||
links: [
|
||||
{
|
||||
title: "Docs",
|
||||
items: [
|
||||
{
|
||||
label: "Dev Environment",
|
||||
to: "/docs/category/how-i-built-this",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Community",
|
||||
items: [
|
||||
{
|
||||
label: "Stack Overflow",
|
||||
href: "https://stackoverflow.com/questions/tagged/docusaurus",
|
||||
},
|
||||
{
|
||||
label: "Discord",
|
||||
href: "https://discordapp.com/invite/docusaurus",
|
||||
},
|
||||
{
|
||||
label: "Twitter",
|
||||
href: "https://twitter.com/docusaurus",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "More",
|
||||
items: [
|
||||
{
|
||||
label: "Blog",
|
||||
to: "/blog",
|
||||
},
|
||||
{
|
||||
label: "GitHub",
|
||||
href: "https://github.com/facebook/docusaurus",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
copyright: `Copyright © ${new Date().getFullYear()} My Project, Inc. Built with Docusaurus.`,
|
||||
},
|
||||
prism: {
|
||||
theme: prismThemes.github,
|
||||
darkTheme: prismThemes.dracula,
|
||||
additionalLanguages: ["nix", "elixir"],
|
||||
},
|
||||
} satisfies Preset.ThemeConfig,
|
||||
};
|
||||
|
||||
export default config;
|
||||
15871
apps/docs/package-lock.json
generated
@ -1,55 +0,0 @@
|
||||
{
|
||||
"name": "@lifetracker/docs",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"docusaurus": "docusaurus",
|
||||
"dev": "docusaurus start --port 3001",
|
||||
"build": "docusaurus build",
|
||||
"swizzle": "docusaurus swizzle",
|
||||
"deploy": "docusaurus deploy",
|
||||
"clear": "docusaurus clear",
|
||||
"serve": "docusaurus serve",
|
||||
"write-translations": "docusaurus write-translations",
|
||||
"write-heading-ids": "docusaurus write-heading-ids",
|
||||
"typecheck": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@lifetracker/ui": "workspace:*",
|
||||
"@docusaurus/core": "3.5.2",
|
||||
"@docusaurus/preset-classic": "3.5.2",
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"clsx": "^2.0.0",
|
||||
"prism-react-renderer": "^2.3.0",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "3.5.2",
|
||||
"@docusaurus/tsconfig": "3.5.2",
|
||||
"@docusaurus/types": "3.5.2",
|
||||
"@lifetracker/eslint-config": "workspace:*",
|
||||
"@lifetracker/typescript-config": "workspace:*",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "14.2.6",
|
||||
"typescript": "^5"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.5%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 3 chrome version",
|
||||
"last 3 firefox version",
|
||||
"last 5 safari version"
|
||||
]
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0"
|
||||
}
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
import type { SidebarsConfig } from "@docusaurus/plugin-content-docs";
|
||||
|
||||
/**
|
||||
* Creating a sidebar enables you to:
|
||||
- create an ordered group of docs
|
||||
- render a sidebar for each doc of that group
|
||||
- provide next/previous navigation
|
||||
|
||||
The sidebars can be generated from the filesystem, or explicitly defined here.
|
||||
|
||||
Create as many sidebars as you want.
|
||||
*/
|
||||
const sidebars: SidebarsConfig = {
|
||||
// By default, Docusaurus generates a sidebar from the docs folder structure
|
||||
tutorialSidebar: [{ type: "autogenerated", dirName: "." }],
|
||||
|
||||
// But you can create a sidebar manually
|
||||
/*
|
||||
tutorialSidebar: [
|
||||
'intro',
|
||||
'hello',
|
||||
{
|
||||
type: 'category',
|
||||
label: 'Tutorial',
|
||||
items: ['tutorial-basics/create-a-document'],
|
||||
},
|
||||
],
|
||||
*/
|
||||
};
|
||||
|
||||
export default sidebars;
|
||||
@ -1,70 +0,0 @@
|
||||
import clsx from "clsx";
|
||||
import Heading from "@theme/Heading";
|
||||
import styles from "./styles.module.css";
|
||||
|
||||
type FeatureItem = {
|
||||
title: string;
|
||||
Svg: React.ComponentType<React.ComponentProps<"svg">>;
|
||||
description: JSX.Element;
|
||||
};
|
||||
|
||||
const FeatureList: FeatureItem[] = [
|
||||
{
|
||||
title: "Easy to Use",
|
||||
Svg: require("@site/static/img/undraw_docusaurus_mountain.svg").default,
|
||||
description: (
|
||||
<>
|
||||
Docusaurus was designed from the ground up to be easily installed and
|
||||
used to get your website up and running quickly.
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Focus on What Matters",
|
||||
Svg: require("@site/static/img/undraw_docusaurus_tree.svg").default,
|
||||
description: (
|
||||
<>
|
||||
Docusaurus lets you focus on your docs, and we'll do the chores. Go
|
||||
ahead and move your docs into the <code>docs</code> directory.
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Powered by React",
|
||||
Svg: require("@site/static/img/undraw_docusaurus_react.svg").default,
|
||||
description: (
|
||||
<>
|
||||
Extend or customize your website layout by reusing React. Docusaurus can
|
||||
be extended while reusing the same header and footer.
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
function Feature({ title, Svg, description }: FeatureItem) {
|
||||
return (
|
||||
<div className={clsx("col col--4")}>
|
||||
<div className="text--center">
|
||||
<Svg className={styles.featureSvg} role="img" />
|
||||
</div>
|
||||
<div className="text--center padding-horiz--md">
|
||||
<Heading as="h3">{title}</Heading>
|
||||
<p>{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function HomepageFeatures(): JSX.Element {
|
||||
return (
|
||||
<section className={styles.features}>
|
||||
<div className="container">
|
||||
<div className="row">
|
||||
{FeatureList.map((props, idx) => (
|
||||
<Feature key={idx} {...props} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
.features {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 2rem 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.featureSvg {
|
||||
height: 200px;
|
||||
width: 200px;
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
/**
|
||||
* Any CSS included here will be global. The classic template
|
||||
* bundles Infima by default. Infima is a CSS framework designed to
|
||||
* work well for content-centric websites.
|
||||
*/
|
||||
|
||||
/* You can override the default Infima variables here. */
|
||||
:root {
|
||||
--ifm-color-primary: #2e8555;
|
||||
--ifm-color-primary-dark: #29784c;
|
||||
--ifm-color-primary-darker: #277148;
|
||||
--ifm-color-primary-darkest: #205d3b;
|
||||
--ifm-color-primary-light: #33925d;
|
||||
--ifm-color-primary-lighter: #359962;
|
||||
--ifm-color-primary-lightest: #3cad6e;
|
||||
--ifm-code-font-size: 95%;
|
||||
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* For readability concerns, you should choose a lighter palette in dark mode. */
|
||||
[data-theme='dark'] {
|
||||
--ifm-color-primary: #25c2a0;
|
||||
--ifm-color-primary-dark: #21af90;
|
||||
--ifm-color-primary-darker: #1fa588;
|
||||
--ifm-color-primary-darkest: #1a8870;
|
||||
--ifm-color-primary-light: #29d5b0;
|
||||
--ifm-color-primary-lighter: #32d8b4;
|
||||
--ifm-color-primary-lightest: #4fddbf;
|
||||
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
/**
|
||||
* CSS files with the .module.css suffix will be treated as CSS modules
|
||||
* and scoped locally.
|
||||
*/
|
||||
|
||||
.heroBanner {
|
||||
padding: 4rem 0;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 996px) {
|
||||
.heroBanner {
|
||||
padding: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
@ -1,45 +0,0 @@
|
||||
import clsx from "clsx";
|
||||
import Link from "@docusaurus/Link";
|
||||
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
|
||||
import Layout from "@theme/Layout";
|
||||
import HomepageFeatures from "@site/src/components/HomepageFeatures";
|
||||
import Heading from "@theme/Heading";
|
||||
|
||||
import styles from "./index.module.css";
|
||||
|
||||
function HomepageHeader() {
|
||||
const { siteConfig } = useDocusaurusContext();
|
||||
return (
|
||||
<header className={clsx("hero hero--primary", styles.heroBanner)}>
|
||||
<div className="container">
|
||||
<Heading as="h1" className="hero__title">
|
||||
{siteConfig.title}
|
||||
</Heading>
|
||||
<p className="hero__subtitle">{siteConfig.tagline}</p>
|
||||
<div className={styles.buttons}>
|
||||
<Link
|
||||
className="button button--secondary button--lg"
|
||||
to="/docs/category/how-i-built-this"
|
||||
>
|
||||
Jump on in
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Home(): JSX.Element {
|
||||
const { siteConfig } = useDocusaurusContext();
|
||||
return (
|
||||
<Layout
|
||||
title={`Hello from ${siteConfig.title}`}
|
||||
description="Description will go into a meta tag in <head />"
|
||||
>
|
||||
<HomepageHeader />
|
||||
<main>
|
||||
<HomepageFeatures />
|
||||
</main>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
---
|
||||
title: Markdown page example
|
||||
---
|
||||
|
||||
# Markdown page example
|
||||
|
||||
You don't need React to write simple standalone pages.
|
||||
0
apps/docs/static/.nojekyll
vendored
BIN
apps/docs/static/img/docusaurus-social-card.jpg
vendored
|
Before Width: | Height: | Size: 54 KiB |
BIN
apps/docs/static/img/docusaurus.png
vendored
|
Before Width: | Height: | Size: 5.0 KiB |
BIN
apps/docs/static/img/favicon.ico
vendored
|
Before Width: | Height: | Size: 3.5 KiB |
1
apps/docs/static/img/logo.svg
vendored
|
Before Width: | Height: | Size: 6.3 KiB |
171
apps/docs/static/img/undraw_docusaurus_mountain.svg
vendored
@ -1,171 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1088" height="687.962" viewBox="0 0 1088 687.962">
|
||||
<title>Easy to Use</title>
|
||||
<g id="Group_12" data-name="Group 12" transform="translate(-57 -56)">
|
||||
<g id="Group_11" data-name="Group 11" transform="translate(57 56)">
|
||||
<path id="Path_83" data-name="Path 83" d="M1017.81,560.461c-5.27,45.15-16.22,81.4-31.25,110.31-20,38.52-54.21,54.04-84.77,70.28a193.275,193.275,0,0,1-27.46,11.94c-55.61,19.3-117.85,14.18-166.74,3.99a657.282,657.282,0,0,0-104.09-13.16q-14.97-.675-29.97-.67c-15.42.02-293.07,5.29-360.67-131.57-16.69-33.76-28.13-75-32.24-125.27-11.63-142.12,52.29-235.46,134.74-296.47,155.97-115.41,369.76-110.57,523.43,7.88C941.15,276.621,1036.99,396.031,1017.81,560.461Z" transform="translate(-56 -106.019)" fill="#3f3d56"/>
|
||||
<path id="Path_84" data-name="Path 84" d="M986.56,670.771c-20,38.52-47.21,64.04-77.77,80.28a193.272,193.272,0,0,1-27.46,11.94c-55.61,19.3-117.85,14.18-166.74,3.99a657.3,657.3,0,0,0-104.09-13.16q-14.97-.675-29.97-.67-23.13.03-46.25,1.72c-100.17,7.36-253.82-6.43-321.42-143.29L382,283.981,444.95,445.6l20.09,51.59,55.37-75.98L549,381.981l130.2,149.27,36.8-81.27L970.78,657.9l14.21,11.59Z" transform="translate(-56 -106.019)" fill="#f2f2f2"/>
|
||||
<path id="Path_85" data-name="Path 85" d="M302,282.962l26-57,36,83-31-60Z" opacity="0.1"/>
|
||||
<path id="Path_86" data-name="Path 86" d="M610.5,753.821q-14.97-.675-29.97-.67L465.04,497.191Z" transform="translate(-56 -106.019)" opacity="0.1"/>
|
||||
<path id="Path_87" data-name="Path 87" d="M464.411,315.191,493,292.962l130,150-132-128Z" opacity="0.1"/>
|
||||
<path id="Path_88" data-name="Path 88" d="M908.79,751.051a193.265,193.265,0,0,1-27.46,11.94L679.2,531.251Z" transform="translate(-56 -106.019)" opacity="0.1"/>
|
||||
<circle id="Ellipse_11" data-name="Ellipse 11" cx="3" cy="3" r="3" transform="translate(479 98.962)" fill="#f2f2f2"/>
|
||||
<circle id="Ellipse_12" data-name="Ellipse 12" cx="3" cy="3" r="3" transform="translate(396 201.962)" fill="#f2f2f2"/>
|
||||
<circle id="Ellipse_13" data-name="Ellipse 13" cx="2" cy="2" r="2" transform="translate(600 220.962)" fill="#f2f2f2"/>
|
||||
<circle id="Ellipse_14" data-name="Ellipse 14" cx="2" cy="2" r="2" transform="translate(180 265.962)" fill="#f2f2f2"/>
|
||||
<circle id="Ellipse_15" data-name="Ellipse 15" cx="2" cy="2" r="2" transform="translate(612 96.962)" fill="#f2f2f2"/>
|
||||
<circle id="Ellipse_16" data-name="Ellipse 16" cx="2" cy="2" r="2" transform="translate(736 192.962)" fill="#f2f2f2"/>
|
||||
<circle id="Ellipse_17" data-name="Ellipse 17" cx="2" cy="2" r="2" transform="translate(858 344.962)" fill="#f2f2f2"/>
|
||||
<path id="Path_89" data-name="Path 89" d="M306,121.222h-2.76v-2.76h-1.48v2.76H299V122.7h2.76v2.759h1.48V122.7H306Z" fill="#f2f2f2"/>
|
||||
<path id="Path_90" data-name="Path 90" d="M848,424.222h-2.76v-2.76h-1.48v2.76H841V425.7h2.76v2.759h1.48V425.7H848Z" fill="#f2f2f2"/>
|
||||
<path id="Path_91" data-name="Path 91" d="M1144,719.981c0,16.569-243.557,74-544,74s-544-57.431-544-74,243.557,14,544,14S1144,703.413,1144,719.981Z" transform="translate(-56 -106.019)" fill="#3f3d56"/>
|
||||
<path id="Path_92" data-name="Path 92" d="M1144,719.981c0,16.569-243.557,74-544,74s-544-57.431-544-74,243.557,14,544,14S1144,703.413,1144,719.981Z" transform="translate(-56 -106.019)" opacity="0.1"/>
|
||||
<ellipse id="Ellipse_18" data-name="Ellipse 18" cx="544" cy="30" rx="544" ry="30" transform="translate(0 583.962)" fill="#3f3d56"/>
|
||||
<path id="Path_93" data-name="Path 93" d="M624,677.981c0,33.137-14.775,24-33,24s-33,9.137-33-24,33-96,33-96S624,644.844,624,677.981Z" transform="translate(-56 -106.019)" fill="#ff6584"/>
|
||||
<path id="Path_94" data-name="Path 94" d="M606,690.66c0,15.062-6.716,10.909-15,10.909s-15,4.153-15-10.909,15-43.636,15-43.636S606,675.6,606,690.66Z" transform="translate(-56 -106.019)" opacity="0.1"/>
|
||||
<rect id="Rectangle_97" data-name="Rectangle 97" width="92" height="18" rx="9" transform="translate(489 604.962)" fill="#2f2e41"/>
|
||||
<rect id="Rectangle_98" data-name="Rectangle 98" width="92" height="18" rx="9" transform="translate(489 586.962)" fill="#2f2e41"/>
|
||||
<path id="Path_95" data-name="Path 95" d="M193,596.547c0,55.343,34.719,100.126,77.626,100.126" transform="translate(-56 -106.019)" fill="#3f3d56"/>
|
||||
<path id="Path_96" data-name="Path 96" d="M270.626,696.673c0-55.965,38.745-101.251,86.626-101.251" transform="translate(-56 -106.019)" fill="#6c63ff"/>
|
||||
<path id="Path_97" data-name="Path 97" d="M221.125,601.564c0,52.57,22.14,95.109,49.5,95.109" transform="translate(-56 -106.019)" fill="#6c63ff"/>
|
||||
<path id="Path_98" data-name="Path 98" d="M270.626,696.673c0-71.511,44.783-129.377,100.126-129.377" transform="translate(-56 -106.019)" fill="#3f3d56"/>
|
||||
<path id="Path_99" data-name="Path 99" d="M254.3,697.379s11.009-.339,14.326-2.7,16.934-5.183,17.757-1.395,16.544,18.844,4.115,18.945-28.879-1.936-32.19-3.953S254.3,697.379,254.3,697.379Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
|
||||
<path id="Path_100" data-name="Path 100" d="M290.716,710.909c-12.429.1-28.879-1.936-32.19-3.953-2.522-1.536-3.527-7.048-3.863-9.591l-.368.014s.7,8.879,4.009,10.9,19.761,4.053,32.19,3.953c3.588-.029,4.827-1.305,4.759-3.2C294.755,710.174,293.386,710.887,290.716,710.909Z" transform="translate(-56 -106.019)" opacity="0.2"/>
|
||||
<path id="Path_101" data-name="Path 101" d="M777.429,633.081c0,38.029,23.857,68.8,53.341,68.8" transform="translate(-56 -106.019)" fill="#3f3d56"/>
|
||||
<path id="Path_102" data-name="Path 102" d="M830.769,701.882c0-38.456,26.623-69.575,59.525-69.575" transform="translate(-56 -106.019)" fill="#6c63ff"/>
|
||||
<path id="Path_103" data-name="Path 103" d="M796.755,636.528c0,36.124,15.213,65.354,34.014,65.354" transform="translate(-56 -106.019)" fill="#6c63ff"/>
|
||||
<path id="Path_104" data-name="Path 104" d="M830.769,701.882c0-49.139,30.773-88.9,68.8-88.9" transform="translate(-56 -106.019)" fill="#3f3d56"/>
|
||||
<path id="Path_105" data-name="Path 105" d="M819.548,702.367s7.565-.233,9.844-1.856,11.636-3.562,12.2-.958,11.368,12.949,2.828,13.018-19.844-1.33-22.119-2.716S819.548,702.367,819.548,702.367Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
|
||||
<path id="Path_106" data-name="Path 106" d="M844.574,711.664c-8.54.069-19.844-1.33-22.119-2.716-1.733-1.056-2.423-4.843-2.654-6.59l-.253.01s.479,6.1,2.755,7.487,13.579,2.785,22.119,2.716c2.465-.02,3.317-.9,3.27-2.2C847.349,711.159,846.409,711.649,844.574,711.664Z" transform="translate(-56 -106.019)" opacity="0.2"/>
|
||||
<path id="Path_107" data-name="Path 107" d="M949.813,724.718s11.36-1.729,14.5-4.591,16.89-7.488,18.217-3.667,19.494,17.447,6.633,19.107-30.153,1.609-33.835-.065S949.813,724.718,949.813,724.718Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
|
||||
<path id="Path_108" data-name="Path 108" d="M989.228,734.173c-12.86,1.659-30.153,1.609-33.835-.065-2.8-1.275-4.535-6.858-5.2-9.45l-.379.061s1.833,9.109,5.516,10.783,20.975,1.725,33.835.065c3.712-.479,4.836-1.956,4.529-3.906C993.319,732.907,991.991,733.817,989.228,734.173Z" transform="translate(-56 -106.019)" opacity="0.2"/>
|
||||
<path id="Path_109" data-name="Path 109" d="M670.26,723.9s9.587-1.459,12.237-3.875,14.255-6.32,15.374-3.095,16.452,14.725,5.6,16.125-25.448,1.358-28.555-.055S670.26,723.9,670.26,723.9Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
|
||||
<path id="Path_110" data-name="Path 110" d="M703.524,731.875c-10.853,1.4-25.448,1.358-28.555-.055-2.367-1.076-3.827-5.788-4.39-7.976l-.32.051s1.547,7.687,4.655,9.1,17.7,1.456,28.555.055c3.133-.4,4.081-1.651,3.822-3.3C706.977,730.807,705.856,731.575,703.524,731.875Z" transform="translate(-56 -106.019)" opacity="0.2"/>
|
||||
<path id="Path_111" data-name="Path 111" d="M178.389,719.109s7.463-1.136,9.527-3.016,11.1-4.92,11.969-2.409,12.808,11.463,4.358,12.553-19.811,1.057-22.23-.043S178.389,719.109,178.389,719.109Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
|
||||
<path id="Path_112" data-name="Path 112" d="M204.285,725.321c-8.449,1.09-19.811,1.057-22.23-.043-1.842-.838-2.979-4.506-3.417-6.209l-.249.04s1.2,5.984,3.624,7.085,13.781,1.133,22.23.043c2.439-.315,3.177-1.285,2.976-2.566C206.973,724.489,206.1,725.087,204.285,725.321Z" transform="translate(-56 -106.019)" opacity="0.2"/>
|
||||
<path id="Path_113" data-name="Path 113" d="M439.7,707.337c0,30.22-42.124,20.873-93.7,20.873s-93.074,9.347-93.074-20.873,42.118-36.793,93.694-36.793S439.7,677.117,439.7,707.337Z" transform="translate(-56 -106.019)" opacity="0.1"/>
|
||||
<path id="Path_114" data-name="Path 114" d="M439.7,699.9c0,30.22-42.124,20.873-93.7,20.873s-93.074,9.347-93.074-20.873S295.04,663.1,346.616,663.1,439.7,669.676,439.7,699.9Z" transform="translate(-56 -106.019)" fill="#3f3d56"/>
|
||||
</g>
|
||||
<g id="docusaurus_keytar" transform="translate(312.271 493.733)">
|
||||
<path id="Path_40" data-name="Path 40" d="M99,52h91.791V89.153H99Z" transform="translate(5.904 -14.001)" fill="#fff" fill-rule="evenodd"/>
|
||||
<path id="Path_41" data-name="Path 41" d="M24.855,163.927A21.828,21.828,0,0,1,5.947,153a21.829,21.829,0,0,0,18.908,32.782H46.71V163.927Z" transform="translate(-3 -4.634)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_42" data-name="Path 42" d="M121.861,61.1l76.514-4.782V45.39A21.854,21.854,0,0,0,176.52,23.535H78.173L75.441,18.8a3.154,3.154,0,0,0-5.464,0l-2.732,4.732L64.513,18.8a3.154,3.154,0,0,0-5.464,0l-2.732,4.732L53.586,18.8a3.154,3.154,0,0,0-5.464,0L45.39,23.535c-.024,0-.046,0-.071,0l-4.526-4.525a3.153,3.153,0,0,0-5.276,1.414l-1.5,5.577-5.674-1.521a3.154,3.154,0,0,0-3.863,3.864L26,34.023l-5.575,1.494a3.155,3.155,0,0,0-1.416,5.278l4.526,4.526c0,.023,0,.046,0,.07L18.8,48.122a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,59.05a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,69.977a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,80.9a3.154,3.154,0,0,0,0,5.464L23.535,89.1,18.8,91.832a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,102.76a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,113.687a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,124.615a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,135.542a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,146.469a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,157.4a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,168.324a3.154,3.154,0,0,0,0,5.464l4.732,2.732A21.854,21.854,0,0,0,45.39,198.375H176.52a21.854,21.854,0,0,0,21.855-21.855V89.1l-76.514-4.782a11.632,11.632,0,0,1,0-23.219" transform="translate(-1.681 -17.226)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_43" data-name="Path 43" d="M143,186.71h32.782V143H143Z" transform="translate(9.984 -5.561)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_44" data-name="Path 44" d="M196.71,159.855a5.438,5.438,0,0,0-.7.07c-.042-.164-.081-.329-.127-.493a5.457,5.457,0,1,0-5.4-9.372q-.181-.185-.366-.367a5.454,5.454,0,1,0-9.384-5.4c-.162-.046-.325-.084-.486-.126a5.467,5.467,0,1,0-10.788,0c-.162.042-.325.08-.486.126a5.457,5.457,0,1,0-9.384,5.4,21.843,21.843,0,1,0,36.421,21.02,5.452,5.452,0,1,0,.7-10.858" transform="translate(10.912 -6.025)" fill="#44d860" fill-rule="evenodd"/>
|
||||
<path id="Path_45" data-name="Path 45" d="M153,124.855h32.782V103H153Z" transform="translate(10.912 -9.271)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_46" data-name="Path 46" d="M194.855,116.765a2.732,2.732,0,1,0,0-5.464,2.811,2.811,0,0,0-.349.035c-.022-.082-.04-.164-.063-.246a2.733,2.733,0,0,0-1.052-5.253,2.7,2.7,0,0,0-1.648.566q-.09-.093-.184-.184a2.7,2.7,0,0,0,.553-1.633,2.732,2.732,0,0,0-5.245-1.07,10.928,10.928,0,1,0,0,21.031,2.732,2.732,0,0,0,5.245-1.07,2.7,2.7,0,0,0-.553-1.633q.093-.09.184-.184a2.7,2.7,0,0,0,1.648.566,2.732,2.732,0,0,0,1.052-5.253c.023-.081.042-.164.063-.246a2.814,2.814,0,0,0,.349.035" transform="translate(12.767 -9.377)" fill="#44d860" fill-rule="evenodd"/>
|
||||
<path id="Path_47" data-name="Path 47" d="M65.087,56.891a2.732,2.732,0,0,1-2.732-2.732,8.2,8.2,0,0,0-16.391,0,2.732,2.732,0,0,1-5.464,0,13.659,13.659,0,0,1,27.319,0,2.732,2.732,0,0,1-2.732,2.732" transform="translate(0.478 -15.068)" fill-rule="evenodd"/>
|
||||
<path id="Path_48" data-name="Path 48" d="M103,191.347h65.565a21.854,21.854,0,0,0,21.855-21.855V93H124.855A21.854,21.854,0,0,0,103,114.855Z" transform="translate(6.275 -10.199)" fill="#ffff50" fill-rule="evenodd"/>
|
||||
<path id="Path_49" data-name="Path 49" d="M173.216,129.787H118.535a1.093,1.093,0,1,1,0-2.185h54.681a1.093,1.093,0,0,1,0,2.185m0,21.855H118.535a1.093,1.093,0,1,1,0-2.186h54.681a1.093,1.093,0,0,1,0,2.186m0,21.855H118.535a1.093,1.093,0,1,1,0-2.185h54.681a1.093,1.093,0,0,1,0,2.185m0-54.434H118.535a1.093,1.093,0,1,1,0-2.185h54.681a1.093,1.093,0,0,1,0,2.185m0,21.652H118.535a1.093,1.093,0,1,1,0-2.186h54.681a1.093,1.093,0,0,1,0,2.186m0,21.855H118.535a1.093,1.093,0,1,1,0-2.186h54.681a1.093,1.093,0,0,1,0,2.186M189.585,61.611c-.013,0-.024-.007-.037-.005-3.377.115-4.974,3.492-6.384,6.472-1.471,3.114-2.608,5.139-4.473,5.078-2.064-.074-3.244-2.406-4.494-4.874-1.436-2.835-3.075-6.049-6.516-5.929-3.329.114-4.932,3.053-6.346,5.646-1.5,2.762-2.529,4.442-4.5,4.364-2.106-.076-3.225-1.972-4.52-4.167-1.444-2.443-3.112-5.191-6.487-5.1-3.272.113-4.879,2.606-6.3,4.808-1.5,2.328-2.552,3.746-4.551,3.662-2.156-.076-3.27-1.65-4.558-3.472-1.447-2.047-3.077-4.363-6.442-4.251-3.2.109-4.807,2.153-6.224,3.954-1.346,1.709-2.4,3.062-4.621,2.977a1.093,1.093,0,0,0-.079,2.186c3.3.11,4.967-1.967,6.417-3.81,1.286-1.635,2.4-3.045,4.582-3.12,2.1-.09,3.091,1.218,4.584,3.327,1.417,2,3.026,4.277,6.263,4.394,3.391.114,5.022-2.42,6.467-4.663,1.292-2,2.406-3.734,4.535-3.807,1.959-.073,3.026,1.475,4.529,4.022,1.417,2.4,3.023,5.121,6.324,5.241,3.415.118,5.064-2.863,6.5-5.5,1.245-2.282,2.419-4.437,4.5-4.509,1.959-.046,2.981,1.743,4.492,4.732,1.412,2.79,3.013,5.95,6.365,6.071l.185,0c3.348,0,4.937-3.36,6.343-6.331,1.245-2.634,2.423-5.114,4.444-5.216Z" transform="translate(7.109 -13.11)" fill-rule="evenodd"/>
|
||||
<path id="Path_50" data-name="Path 50" d="M83,186.71h43.71V143H83Z" transform="translate(4.42 -5.561)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<g id="Group_8" data-name="Group 8" transform="matrix(0.966, -0.259, 0.259, 0.966, 109.327, 91.085)">
|
||||
<rect id="Rectangle_3" data-name="Rectangle 3" width="92.361" height="36.462" rx="2" transform="translate(0 0)" fill="#d8d8d8"/>
|
||||
<g id="Group_2" data-name="Group 2" transform="translate(1.531 23.03)">
|
||||
<rect id="Rectangle_4" data-name="Rectangle 4" width="5.336" height="5.336" rx="1" transform="translate(16.797 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_5" data-name="Rectangle 5" width="5.336" height="5.336" rx="1" transform="translate(23.12 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_6" data-name="Rectangle 6" width="5.336" height="5.336" rx="1" transform="translate(29.444 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_7" data-name="Rectangle 7" width="5.336" height="5.336" rx="1" transform="translate(35.768 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_8" data-name="Rectangle 8" width="5.336" height="5.336" rx="1" transform="translate(42.091 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_9" data-name="Rectangle 9" width="5.336" height="5.336" rx="1" transform="translate(48.415 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_10" data-name="Rectangle 10" width="5.336" height="5.336" rx="1" transform="translate(54.739 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_11" data-name="Rectangle 11" width="5.336" height="5.336" rx="1" transform="translate(61.063 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_12" data-name="Rectangle 12" width="5.336" height="5.336" rx="1" transform="translate(67.386 0)" fill="#4a4a4a"/>
|
||||
<path id="Path_51" data-name="Path 51" d="M1.093,0H14.518a1.093,1.093,0,0,1,1.093,1.093V4.243a1.093,1.093,0,0,1-1.093,1.093H1.093A1.093,1.093,0,0,1,0,4.243V1.093A1.093,1.093,0,0,1,1.093,0ZM75,0H88.426a1.093,1.093,0,0,1,1.093,1.093V4.243a1.093,1.093,0,0,1-1.093,1.093H75a1.093,1.093,0,0,1-1.093-1.093V1.093A1.093,1.093,0,0,1,75,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<g id="Group_3" data-name="Group 3" transform="translate(1.531 10.261)">
|
||||
<path id="Path_52" data-name="Path 52" d="M1.093,0H6.218A1.093,1.093,0,0,1,7.31,1.093V4.242A1.093,1.093,0,0,1,6.218,5.335H1.093A1.093,1.093,0,0,1,0,4.242V1.093A1.093,1.093,0,0,1,1.093,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
<rect id="Rectangle_13" data-name="Rectangle 13" width="5.336" height="5.336" rx="1" transform="translate(8.299 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_14" data-name="Rectangle 14" width="5.336" height="5.336" rx="1" transform="translate(14.623 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_15" data-name="Rectangle 15" width="5.336" height="5.336" rx="1" transform="translate(20.947 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_16" data-name="Rectangle 16" width="5.336" height="5.336" rx="1" transform="translate(27.271 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_17" data-name="Rectangle 17" width="5.336" height="5.336" rx="1" transform="translate(33.594 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_18" data-name="Rectangle 18" width="5.336" height="5.336" rx="1" transform="translate(39.918 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_19" data-name="Rectangle 19" width="5.336" height="5.336" rx="1" transform="translate(46.242 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_20" data-name="Rectangle 20" width="5.336" height="5.336" rx="1" transform="translate(52.565 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_21" data-name="Rectangle 21" width="5.336" height="5.336" rx="1" transform="translate(58.888 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_22" data-name="Rectangle 22" width="5.336" height="5.336" rx="1" transform="translate(65.212 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_23" data-name="Rectangle 23" width="5.336" height="5.336" rx="1" transform="translate(71.536 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_24" data-name="Rectangle 24" width="5.336" height="5.336" rx="1" transform="translate(77.859 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_25" data-name="Rectangle 25" width="5.336" height="5.336" rx="1" transform="translate(84.183 0)" fill="#4a4a4a"/>
|
||||
</g>
|
||||
<g id="Group_4" data-name="Group 4" transform="translate(91.05 9.546) rotate(180)">
|
||||
<path id="Path_53" data-name="Path 53" d="M1.093,0H6.219A1.093,1.093,0,0,1,7.312,1.093v3.15A1.093,1.093,0,0,1,6.219,5.336H1.093A1.093,1.093,0,0,1,0,4.243V1.093A1.093,1.093,0,0,1,1.093,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
<rect id="Rectangle_26" data-name="Rectangle 26" width="5.336" height="5.336" rx="1" transform="translate(8.299 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_27" data-name="Rectangle 27" width="5.336" height="5.336" rx="1" transform="translate(14.623 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_28" data-name="Rectangle 28" width="5.336" height="5.336" rx="1" transform="translate(20.947 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_29" data-name="Rectangle 29" width="5.336" height="5.336" rx="1" transform="translate(27.271 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_30" data-name="Rectangle 30" width="5.336" height="5.336" rx="1" transform="translate(33.594 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_31" data-name="Rectangle 31" width="5.336" height="5.336" rx="1" transform="translate(39.918 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_32" data-name="Rectangle 32" width="5.336" height="5.336" rx="1" transform="translate(46.242 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_33" data-name="Rectangle 33" width="5.336" height="5.336" rx="1" transform="translate(52.565 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_34" data-name="Rectangle 34" width="5.336" height="5.336" rx="1" transform="translate(58.889 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_35" data-name="Rectangle 35" width="5.336" height="5.336" rx="1" transform="translate(65.213 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_36" data-name="Rectangle 36" width="5.336" height="5.336" rx="1" transform="translate(71.537 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_37" data-name="Rectangle 37" width="5.336" height="5.336" rx="1" transform="translate(77.86 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_38" data-name="Rectangle 38" width="5.336" height="5.336" rx="1" transform="translate(84.183 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_39" data-name="Rectangle 39" width="5.336" height="5.336" rx="1" transform="translate(8.299 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_40" data-name="Rectangle 40" width="5.336" height="5.336" rx="1" transform="translate(14.623 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_41" data-name="Rectangle 41" width="5.336" height="5.336" rx="1" transform="translate(20.947 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_42" data-name="Rectangle 42" width="5.336" height="5.336" rx="1" transform="translate(27.271 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_43" data-name="Rectangle 43" width="5.336" height="5.336" rx="1" transform="translate(33.594 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_44" data-name="Rectangle 44" width="5.336" height="5.336" rx="1" transform="translate(39.918 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_45" data-name="Rectangle 45" width="5.336" height="5.336" rx="1" transform="translate(46.242 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_46" data-name="Rectangle 46" width="5.336" height="5.336" rx="1" transform="translate(52.565 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_47" data-name="Rectangle 47" width="5.336" height="5.336" rx="1" transform="translate(58.889 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_48" data-name="Rectangle 48" width="5.336" height="5.336" rx="1" transform="translate(65.213 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_49" data-name="Rectangle 49" width="5.336" height="5.336" rx="1" transform="translate(71.537 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_50" data-name="Rectangle 50" width="5.336" height="5.336" rx="1" transform="translate(77.86 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_51" data-name="Rectangle 51" width="5.336" height="5.336" rx="1" transform="translate(84.183 0)" fill="#4a4a4a"/>
|
||||
</g>
|
||||
<g id="Group_6" data-name="Group 6" transform="translate(1.531 16.584)">
|
||||
<path id="Path_54" data-name="Path 54" d="M1.093,0h7.3A1.093,1.093,0,0,1,9.485,1.093v3.15A1.093,1.093,0,0,1,8.392,5.336h-7.3A1.093,1.093,0,0,1,0,4.243V1.094A1.093,1.093,0,0,1,1.093,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
<g id="Group_5" data-name="Group 5" transform="translate(10.671 0)">
|
||||
<rect id="Rectangle_52" data-name="Rectangle 52" width="5.336" height="5.336" rx="1" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_53" data-name="Rectangle 53" width="5.336" height="5.336" rx="1" transform="translate(6.324 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_54" data-name="Rectangle 54" width="5.336" height="5.336" rx="1" transform="translate(12.647 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_55" data-name="Rectangle 55" width="5.336" height="5.336" rx="1" transform="translate(18.971 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_56" data-name="Rectangle 56" width="5.336" height="5.336" rx="1" transform="translate(25.295 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_57" data-name="Rectangle 57" width="5.336" height="5.336" rx="1" transform="translate(31.619 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_58" data-name="Rectangle 58" width="5.336" height="5.336" rx="1" transform="translate(37.942 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_59" data-name="Rectangle 59" width="5.336" height="5.336" rx="1" transform="translate(44.265 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_60" data-name="Rectangle 60" width="5.336" height="5.336" rx="1" transform="translate(50.589 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_61" data-name="Rectangle 61" width="5.336" height="5.336" rx="1" transform="translate(56.912 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_62" data-name="Rectangle 62" width="5.336" height="5.336" rx="1" transform="translate(63.236 0)" fill="#4a4a4a"/>
|
||||
</g>
|
||||
<path id="Path_55" data-name="Path 55" d="M1.094,0H8A1.093,1.093,0,0,1,9.091,1.093v3.15A1.093,1.093,0,0,1,8,5.336H1.093A1.093,1.093,0,0,1,0,4.243V1.094A1.093,1.093,0,0,1,1.093,0Z" transform="translate(80.428 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<g id="Group_7" data-name="Group 7" transform="translate(1.531 29.627)">
|
||||
<rect id="Rectangle_63" data-name="Rectangle 63" width="5.336" height="5.336" rx="1" transform="translate(0 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_64" data-name="Rectangle 64" width="5.336" height="5.336" rx="1" transform="translate(6.324 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_65" data-name="Rectangle 65" width="5.336" height="5.336" rx="1" transform="translate(12.647 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_66" data-name="Rectangle 66" width="5.336" height="5.336" rx="1" transform="translate(18.971 0)" fill="#4a4a4a"/>
|
||||
<path id="Path_56" data-name="Path 56" d="M1.093,0H31.515a1.093,1.093,0,0,1,1.093,1.093V4.244a1.093,1.093,0,0,1-1.093,1.093H1.093A1.093,1.093,0,0,1,0,4.244V1.093A1.093,1.093,0,0,1,1.093,0ZM34.687,0h3.942a1.093,1.093,0,0,1,1.093,1.093V4.244a1.093,1.093,0,0,1-1.093,1.093H34.687a1.093,1.093,0,0,1-1.093-1.093V1.093A1.093,1.093,0,0,1,34.687,0Z" transform="translate(25.294 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
<rect id="Rectangle_67" data-name="Rectangle 67" width="5.336" height="5.336" rx="1" transform="translate(66.003 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_68" data-name="Rectangle 68" width="5.336" height="5.336" rx="1" transform="translate(72.327 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_69" data-name="Rectangle 69" width="5.336" height="5.336" rx="1" transform="translate(84.183 0)" fill="#4a4a4a"/>
|
||||
<path id="Path_57" data-name="Path 57" d="M5.336,0V1.18A1.093,1.093,0,0,1,4.243,2.273H1.093A1.093,1.093,0,0,1,0,1.18V0Z" transform="translate(83.59 2.273) rotate(180)" fill="#4a4a4a"/>
|
||||
<path id="Path_58" data-name="Path 58" d="M5.336,0V1.18A1.093,1.093,0,0,1,4.243,2.273H1.093A1.093,1.093,0,0,1,0,1.18V0Z" transform="translate(78.255 3.063)" fill="#4a4a4a"/>
|
||||
</g>
|
||||
<rect id="Rectangle_70" data-name="Rectangle 70" width="88.927" height="2.371" rx="1.085" transform="translate(1.925 1.17)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_71" data-name="Rectangle 71" width="4.986" height="1.581" rx="0.723" transform="translate(4.1 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_72" data-name="Rectangle 72" width="4.986" height="1.581" rx="0.723" transform="translate(10.923 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_73" data-name="Rectangle 73" width="4.986" height="1.581" rx="0.723" transform="translate(16.173 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_74" data-name="Rectangle 74" width="4.986" height="1.581" rx="0.723" transform="translate(21.421 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_75" data-name="Rectangle 75" width="4.986" height="1.581" rx="0.723" transform="translate(26.671 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_76" data-name="Rectangle 76" width="4.986" height="1.581" rx="0.723" transform="translate(33.232 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_77" data-name="Rectangle 77" width="4.986" height="1.581" rx="0.723" transform="translate(38.48 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_78" data-name="Rectangle 78" width="4.986" height="1.581" rx="0.723" transform="translate(43.73 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_79" data-name="Rectangle 79" width="4.986" height="1.581" rx="0.723" transform="translate(48.978 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_80" data-name="Rectangle 80" width="4.986" height="1.581" rx="0.723" transform="translate(55.54 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_81" data-name="Rectangle 81" width="4.986" height="1.581" rx="0.723" transform="translate(60.788 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_82" data-name="Rectangle 82" width="4.986" height="1.581" rx="0.723" transform="translate(66.038 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_83" data-name="Rectangle 83" width="4.986" height="1.581" rx="0.723" transform="translate(72.599 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_84" data-name="Rectangle 84" width="4.986" height="1.581" rx="0.723" transform="translate(77.847 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_85" data-name="Rectangle 85" width="4.986" height="1.581" rx="0.723" transform="translate(83.097 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
</g>
|
||||
<path id="Path_59" data-name="Path 59" d="M146.71,159.855a5.439,5.439,0,0,0-.7.07c-.042-.164-.081-.329-.127-.493a5.457,5.457,0,1,0-5.4-9.372q-.181-.185-.366-.367a5.454,5.454,0,1,0-9.384-5.4c-.162-.046-.325-.084-.486-.126a5.467,5.467,0,1,0-10.788,0c-.162.042-.325.08-.486.126a5.457,5.457,0,1,0-9.384,5.4,21.843,21.843,0,1,0,36.421,21.02,5.452,5.452,0,1,0,.7-10.858" transform="translate(6.275 -6.025)" fill="#44d860" fill-rule="evenodd"/>
|
||||
<path id="Path_60" data-name="Path 60" d="M83,124.855h43.71V103H83Z" transform="translate(4.42 -9.271)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_61" data-name="Path 61" d="M134.855,116.765a2.732,2.732,0,1,0,0-5.464,2.811,2.811,0,0,0-.349.035c-.022-.082-.04-.164-.063-.246a2.733,2.733,0,0,0-1.052-5.253,2.7,2.7,0,0,0-1.648.566q-.09-.093-.184-.184a2.7,2.7,0,0,0,.553-1.633,2.732,2.732,0,0,0-5.245-1.07,10.928,10.928,0,1,0,0,21.031,2.732,2.732,0,0,0,5.245-1.07,2.7,2.7,0,0,0-.553-1.633q.093-.09.184-.184a2.7,2.7,0,0,0,1.648.566,2.732,2.732,0,0,0,1.052-5.253c.023-.081.042-.164.063-.246a2.811,2.811,0,0,0,.349.035" transform="translate(7.202 -9.377)" fill="#44d860" fill-rule="evenodd"/>
|
||||
<path id="Path_62" data-name="Path 62" d="M143.232,42.33a2.967,2.967,0,0,1-.535-.055,2.754,2.754,0,0,1-.514-.153,2.838,2.838,0,0,1-.471-.251,4.139,4.139,0,0,1-.415-.339,3.2,3.2,0,0,1-.338-.415A2.7,2.7,0,0,1,140.5,39.6a2.968,2.968,0,0,1,.055-.535,3.152,3.152,0,0,1,.152-.514,2.874,2.874,0,0,1,.252-.47,2.633,2.633,0,0,1,.753-.754,2.837,2.837,0,0,1,.471-.251,2.753,2.753,0,0,1,.514-.153,2.527,2.527,0,0,1,1.071,0,2.654,2.654,0,0,1,.983.4,4.139,4.139,0,0,1,.415.339,4.019,4.019,0,0,1,.339.415,2.786,2.786,0,0,1,.251.47,2.864,2.864,0,0,1,.208,1.049,2.77,2.77,0,0,1-.8,1.934,4.139,4.139,0,0,1-.415.339,2.722,2.722,0,0,1-1.519.459m21.855-1.366a2.789,2.789,0,0,1-1.935-.8,4.162,4.162,0,0,1-.338-.415,2.7,2.7,0,0,1-.459-1.519,2.789,2.789,0,0,1,.8-1.934,4.139,4.139,0,0,1,.415-.339,2.838,2.838,0,0,1,.471-.251,2.752,2.752,0,0,1,.514-.153,2.527,2.527,0,0,1,1.071,0,2.654,2.654,0,0,1,.983.4,4.139,4.139,0,0,1,.415.339,2.79,2.79,0,0,1,.8,1.934,3.069,3.069,0,0,1-.055.535,2.779,2.779,0,0,1-.153.514,3.885,3.885,0,0,1-.251.47,4.02,4.02,0,0,1-.339.415,4.138,4.138,0,0,1-.415.339,2.722,2.722,0,0,1-1.519.459" transform="translate(9.753 -15.532)" fill-rule="evenodd"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 31 KiB |
170
apps/docs/static/img/undraw_docusaurus_react.svg
vendored
@ -1,170 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1041.277" height="554.141" viewBox="0 0 1041.277 554.141">
|
||||
<title>Powered by React</title>
|
||||
<g id="Group_24" data-name="Group 24" transform="translate(-440 -263)">
|
||||
<g id="Group_23" data-name="Group 23" transform="translate(439.989 262.965)">
|
||||
<path id="Path_299" data-name="Path 299" d="M1040.82,611.12q-1.74,3.75-3.47,7.4-2.7,5.67-5.33,11.12c-.78,1.61-1.56,3.19-2.32,4.77-8.6,17.57-16.63,33.11-23.45,45.89A73.21,73.21,0,0,1,942.44,719l-151.65,1.65h-1.6l-13,.14-11.12.12-34.1.37h-1.38l-17.36.19h-.53l-107,1.16-95.51,1-11.11.12-69,.75H429l-44.75.48h-.48l-141.5,1.53-42.33.46a87.991,87.991,0,0,1-10.79-.54h0c-1.22-.14-2.44-.3-3.65-.49a87.38,87.38,0,0,1-51.29-27.54C116,678.37,102.75,655,93.85,629.64q-1.93-5.49-3.6-11.12C59.44,514.37,97,380,164.6,290.08q4.25-5.64,8.64-11l.07-.08c20.79-25.52,44.1-46.84,68.93-62,44-26.91,92.75-34.49,140.7-11.9,40.57,19.12,78.45,28.11,115.17,30.55,3.71.24,7.42.42,11.11.53,84.23,2.65,163.17-27.7,255.87-47.29,3.69-.78,7.39-1.55,11.12-2.28,66.13-13.16,139.49-20.1,226.73-5.51a189.089,189.089,0,0,1,26.76,6.4q5.77,1.86,11.12,4c41.64,16.94,64.35,48.24,74,87.46q1.37,5.46,2.37,11.11C1134.3,384.41,1084.19,518.23,1040.82,611.12Z" transform="translate(-79.34 -172.91)" fill="#f2f2f2"/>
|
||||
<path id="Path_300" data-name="Path 300" d="M576.36,618.52a95.21,95.21,0,0,1-1.87,11.12h93.7V618.52Zm-78.25,62.81,11.11-.09V653.77c-3.81-.17-7.52-.34-11.11-.52ZM265.19,618.52v11.12h198.5V618.52ZM1114.87,279h-74V191.51q-5.35-2.17-11.12-4V279H776.21V186.58c-3.73.73-7.43,1.5-11.12,2.28V279H509.22V236.15c-3.69-.11-7.4-.29-11.11-.53V279H242.24V217c-24.83,15.16-48.14,36.48-68.93,62h-.07v.08q-4.4,5.4-8.64,11h8.64V618.52h-83q1.66,5.63,3.6,11.12h79.39v93.62a87,87,0,0,0,12.2,2.79c1.21.19,2.43.35,3.65.49h0a87.991,87.991,0,0,0,10.79.54l42.33-.46v-97H498.11v94.21l11.11-.12V629.64H765.09V721l11.12-.12V629.64H1029.7v4.77c.76-1.58,1.54-3.16,2.32-4.77q2.63-5.45,5.33-11.12,1.73-3.64,3.47-7.4v-321h76.42Q1116.23,284.43,1114.87,279ZM242.24,618.52V290.08H498.11V618.52Zm267,0V290.08H765.09V618.52Zm520.48,0H776.21V290.08H1029.7Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
|
||||
<path id="Path_301" data-name="Path 301" d="M863.09,533.65v13l-151.92,1.4-1.62.03-57.74.53-1.38.02-17.55.15h-.52l-106.98.99L349.77,551.4h-.15l-44.65.42-.48.01-198.4,1.82v-15l46.65-28,93.6-.78,2-.01.66-.01,2-.03,44.94-.37,2.01-.01.64-.01,2-.01L315,509.3l.38-.01,35.55-.3h.29l277.4-2.34,6.79-.05h.68l5.18-.05,37.65-.31,2-.03,1.85-.02h.96l11.71-.09,2.32-.03,3.11-.02,9.75-.09,15.47-.13,2-.02,3.48-.02h.65l74.71-.64Z" fill="#65617d"/>
|
||||
<path id="Path_302" data-name="Path 302" d="M863.09,533.65v13l-151.92,1.4-1.62.03-57.74.53-1.38.02-17.55.15h-.52l-106.98.99L349.77,551.4h-.15l-44.65.42-.48.01-198.4,1.82v-15l46.65-28,93.6-.78,2-.01.66-.01,2-.03,44.94-.37,2.01-.01.64-.01,2-.01L315,509.3l.38-.01,35.55-.3h.29l277.4-2.34,6.79-.05h.68l5.18-.05,37.65-.31,2-.03,1.85-.02h.96l11.71-.09,2.32-.03,3.11-.02,9.75-.09,15.47-.13,2-.02,3.48-.02h.65l74.71-.64Z" opacity="0.2"/>
|
||||
<path id="Path_303" data-name="Path 303" d="M375.44,656.57v24.49a6.13,6.13,0,0,1-3.5,5.54,6,6,0,0,1-2.5.6l-34.9.74a6,6,0,0,1-2.7-.57,6.12,6.12,0,0,1-3.57-5.57V656.57Z" transform="translate(-79.34 -172.91)" fill="#3f3d56"/>
|
||||
<path id="Path_304" data-name="Path 304" d="M375.44,656.57v24.49a6.13,6.13,0,0,1-3.5,5.54,6,6,0,0,1-2.5.6l-34.9.74a6,6,0,0,1-2.7-.57,6.12,6.12,0,0,1-3.57-5.57V656.57Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
|
||||
<path id="Path_305" data-name="Path 305" d="M377.44,656.57v24.49a6.13,6.13,0,0,1-3.5,5.54,6,6,0,0,1-2.5.6l-34.9.74a6,6,0,0,1-2.7-.57,6.12,6.12,0,0,1-3.57-5.57V656.57Z" transform="translate(-79.34 -172.91)" fill="#3f3d56"/>
|
||||
<rect id="Rectangle_137" data-name="Rectangle 137" width="47.17" height="31.5" transform="translate(680.92 483.65)" fill="#3f3d56"/>
|
||||
<rect id="Rectangle_138" data-name="Rectangle 138" width="47.17" height="31.5" transform="translate(680.92 483.65)" opacity="0.1"/>
|
||||
<rect id="Rectangle_139" data-name="Rectangle 139" width="47.17" height="31.5" transform="translate(678.92 483.65)" fill="#3f3d56"/>
|
||||
<path id="Path_306" data-name="Path 306" d="M298.09,483.65v4.97l-47.17,1.26v-6.23Z" opacity="0.1"/>
|
||||
<path id="Path_307" data-name="Path 307" d="M460.69,485.27v168.2a4,4,0,0,1-3.85,3.95l-191.65,5.1h-.05a4,4,0,0,1-3.95-3.95V485.27a4,4,0,0,1,3.95-3.95h191.6a4,4,0,0,1,3.95,3.95Z" transform="translate(-79.34 -172.91)" fill="#65617d"/>
|
||||
<path id="Path_308" data-name="Path 308" d="M265.19,481.32v181.2h-.05a4,4,0,0,1-3.95-3.95V485.27a4,4,0,0,1,3.95-3.95Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
|
||||
<path id="Path_309" data-name="Path 309" d="M194.59,319.15h177.5V467.4l-177.5,4Z" fill="#39374d"/>
|
||||
<path id="Path_310" data-name="Path 310" d="M726.09,483.65v6.41l-47.17-1.26v-5.15Z" opacity="0.1"/>
|
||||
<path id="Path_311" data-name="Path 311" d="M867.69,485.27v173.3a4,4,0,0,1-4,3.95h0L672,657.42a4,4,0,0,1-3.85-3.95V485.27a4,4,0,0,1,3.95-3.95H863.7a4,4,0,0,1,3.99,3.95Z" transform="translate(-79.34 -172.91)" fill="#65617d"/>
|
||||
<path id="Path_312" data-name="Path 312" d="M867.69,485.27v173.3a4,4,0,0,1-4,3.95h0V481.32h0a4,4,0,0,1,4,3.95Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
|
||||
<path id="Path_313" data-name="Path 313" d="M775.59,319.15H598.09V467.4l177.5,4Z" fill="#39374d"/>
|
||||
<path id="Path_314" data-name="Path 314" d="M663.19,485.27v168.2a4,4,0,0,1-3.85,3.95l-191.65,5.1h0a4,4,0,0,1-4-3.95V485.27a4,4,0,0,1,3.95-3.95h191.6A4,4,0,0,1,663.19,485.27Z" transform="translate(-79.34 -172.91)" fill="#65617d"/>
|
||||
<path id="Path_315" data-name="Path 315" d="M397.09,319.15h177.5V467.4l-177.5,4Z" fill="#4267b2"/>
|
||||
<path id="Path_316" data-name="Path 316" d="M863.09,533.65v13l-151.92,1.4-1.62.03-57.74.53-1.38.02-17.55.15h-.52l-106.98.99L349.77,551.4h-.15l-44.65.42-.48.01-198.4,1.82v-15l202.51-1.33h.48l40.99-.28h.19l283.08-1.87h.29l.17-.01h.47l4.79-.03h1.46l74.49-.5,4.4-.02.98-.01Z" opacity="0.1"/>
|
||||
<circle id="Ellipse_111" data-name="Ellipse 111" cx="51.33" cy="51.33" r="51.33" transform="translate(435.93 246.82)" fill="#fbbebe"/>
|
||||
<path id="Path_317" data-name="Path 317" d="M617.94,550.07s-99.5,12-90,0c3.44-4.34,4.39-17.2,4.2-31.85-.06-4.45-.22-9.06-.45-13.65-1.1-22-3.75-43.5-3.75-43.5s87-41,77-8.5c-4,13.13-2.69,31.57.35,48.88.89,5.05,1.92,10,3,14.7a344.66,344.66,0,0,0,9.65,33.92Z" transform="translate(-79.34 -172.91)" fill="#fbbebe"/>
|
||||
<path id="Path_318" data-name="Path 318" d="M585.47,546c11.51-2.13,23.7-6,34.53-1.54,2.85,1.17,5.47,2.88,8.39,3.86s6.12,1.22,9.16,1.91c10.68,2.42,19.34,10.55,24.9,20s8.44,20.14,11.26,30.72l6.9,25.83c6,22.45,12,45.09,13.39,68.3a2437.506,2437.506,0,0,1-250.84,1.43c5.44-10.34,11-21.31,10.54-33s-7.19-23.22-4.76-34.74c1.55-7.34,6.57-13.39,9.64-20.22,8.75-19.52,1.94-45.79,17.32-60.65,6.92-6.68,17-9.21,26.63-8.89,12.28.41,24.85,4.24,37,6.11C555.09,547.48,569.79,548.88,585.47,546Z" transform="translate(-79.34 -172.91)" fill="#ff6584"/>
|
||||
<path id="Path_319" data-name="Path 319" d="M716.37,657.17l-.1,1.43v.1l-.17,2.3-1.33,18.51-1.61,22.3-.46,6.28-1,13.44v.17l-107,1-175.59,1.9v.84h-.14v-1.12l.45-14.36.86-28.06.74-23.79.07-2.37a10.53,10.53,0,0,1,11.42-10.17c4.72.4,10.85.89,18.18,1.41l3,.22c42.33,2.94,120.56,6.74,199.5,2,1.66-.09,3.33-.19,5-.31,12.24-.77,24.47-1.76,36.58-3a10.53,10.53,0,0,1,11.6,11.23Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
|
||||
<path id="Path_320" data-name="Path 320" d="M429.08,725.44v-.84l175.62-1.91,107-1h.3v-.17l1-13.44.43-6,1.64-22.61,1.29-17.9v-.44a10.617,10.617,0,0,0-.11-2.47.3.3,0,0,0,0-.1,10.391,10.391,0,0,0-2-4.64,10.54,10.54,0,0,0-9.42-4c-12.11,1.24-24.34,2.23-36.58,3-1.67.12-3.34.22-5,.31-78.94,4.69-157.17.89-199.5-2l-3-.22c-7.33-.52-13.46-1-18.18-1.41a10.54,10.54,0,0,0-11.24,8.53,11,11,0,0,0-.18,1.64l-.68,22.16L429.54,710l-.44,14.36v1.12Z" transform="translate(-79.34 -172.91)" fill="#3f3d56"/>
|
||||
<path id="Path_321" data-name="Path 321" d="M716.67,664.18l-1.23,15.33-1.83,22.85-.46,5.72-1,12.81-.06.64v.17h0l-.15,1.48.11-1.48h-.29l-107,1-175.65,1.9v-.28l.49-14.36,1-28.06.64-18.65A6.36,6.36,0,0,1,434.3,658a6.25,6.25,0,0,1,3.78-.9c2.1.17,4.68.37,7.69.59,4.89.36,10.92.78,17.94,1.22,13,.82,29.31,1.7,48,2.42,52,2,122.2,2.67,188.88-3.17,3-.26,6.1-.55,9.13-.84a6.26,6.26,0,0,1,3.48.66,5.159,5.159,0,0,1,.86.54,6.14,6.14,0,0,1,2,2.46,3.564,3.564,0,0,1,.25.61A6.279,6.279,0,0,1,716.67,664.18Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
|
||||
<path id="Path_322" data-name="Path 322" d="M377.44,677.87v3.19a6.13,6.13,0,0,1-3.5,5.54l-40.1.77a6.12,6.12,0,0,1-3.57-5.57v-3Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
|
||||
<path id="Path_323" data-name="Path 323" d="M298.59,515.57l-52.25,1V507.9l52.25-1Z" fill="#3f3d56"/>
|
||||
<path id="Path_324" data-name="Path 324" d="M298.59,515.57l-52.25,1V507.9l52.25-1Z" opacity="0.1"/>
|
||||
<path id="Path_325" data-name="Path 325" d="M300.59,515.57l-52.25,1V507.9l52.25-1Z" fill="#3f3d56"/>
|
||||
<path id="Path_326" data-name="Path 326" d="M758.56,679.87v3.19a6.13,6.13,0,0,0,3.5,5.54l40.1.77a6.12,6.12,0,0,0,3.57-5.57v-3Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
|
||||
<path id="Path_327" data-name="Path 327" d="M678.72,517.57l52.25,1V509.9l-52.25-1Z" opacity="0.1"/>
|
||||
<path id="Path_328" data-name="Path 328" d="M676.72,517.57l52.25,1V509.9l-52.25-1Z" fill="#3f3d56"/>
|
||||
<path id="Path_329" data-name="Path 329" d="M534.13,486.79c.08,7-3.16,13.6-5.91,20.07a163.491,163.491,0,0,0-12.66,74.71c.73,11,2.58,22,.73,32.9s-8.43,21.77-19,24.9c17.53,10.45,41.26,9.35,57.76-2.66,8.79-6.4,15.34-15.33,21.75-24.11a97.86,97.86,0,0,1-13.31,44.75A103.43,103.43,0,0,0,637,616.53c4.31-5.81,8.06-12.19,9.72-19.23,3.09-13-1.22-26.51-4.51-39.5a266.055,266.055,0,0,1-6.17-33c-.43-3.56-.78-7.22.1-10.7,1-4.07,3.67-7.51,5.64-11.22,5.6-10.54,5.73-23.3,2.86-34.88s-8.49-22.26-14.06-32.81c-4.46-8.46-9.3-17.31-17.46-22.28-5.1-3.1-11-4.39-16.88-5.64l-25.37-5.43c-5.55-1.19-11.26-2.38-16.87-1.51-9.47,1.48-16.14,8.32-22,15.34-4.59,5.46-15.81,15.71-16.6,22.86-.72,6.59,5.1,17.63,6.09,24.58,1.3,9,2.22,6,7.3,11.52C532,478.05,534.07,482,534.13,486.79Z" transform="translate(-79.34 -172.91)" fill="#3f3d56"/>
|
||||
</g>
|
||||
<g id="docusaurus_keytar" transform="translate(670.271 615.768)">
|
||||
<path id="Path_40" data-name="Path 40" d="M99,52h43.635V69.662H99Z" transform="translate(-49.132 -33.936)" fill="#fff" fill-rule="evenodd"/>
|
||||
<path id="Path_41" data-name="Path 41" d="M13.389,158.195A10.377,10.377,0,0,1,4.4,153a10.377,10.377,0,0,0,8.988,15.584H23.779V158.195Z" transform="translate(-3 -82.47)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_42" data-name="Path 42" d="M66.967,38.083l36.373-2.273V30.615A10.389,10.389,0,0,0,92.95,20.226H46.2l-1.3-2.249a1.5,1.5,0,0,0-2.6,0L41,20.226l-1.3-2.249a1.5,1.5,0,0,0-2.6,0l-1.3,2.249-1.3-2.249a1.5,1.5,0,0,0-2.6,0l-1.3,2.249-.034,0-2.152-2.151a1.5,1.5,0,0,0-2.508.672L25.21,21.4l-2.7-.723a1.5,1.5,0,0,0-1.836,1.837l.722,2.7-2.65.71a1.5,1.5,0,0,0-.673,2.509l2.152,2.152c0,.011,0,.022,0,.033l-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6L20.226,41l-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3A10.389,10.389,0,0,0,30.615,103.34H92.95A10.389,10.389,0,0,0,103.34,92.95V51.393L66.967,49.12a5.53,5.53,0,0,1,0-11.038" transform="translate(-9.836 -17.226)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_43" data-name="Path 43" d="M143,163.779h15.584V143H143Z" transform="translate(-70.275 -77.665)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_44" data-name="Path 44" d="M173.779,148.389a2.582,2.582,0,0,0-.332.033c-.02-.078-.038-.156-.06-.234a2.594,2.594,0,1,0-2.567-4.455q-.086-.088-.174-.175a2.593,2.593,0,1,0-4.461-2.569c-.077-.022-.154-.04-.231-.06a2.6,2.6,0,1,0-5.128,0c-.077.02-.154.038-.231.06a2.594,2.594,0,1,0-4.461,2.569,10.384,10.384,0,1,0,17.314,9.992,2.592,2.592,0,1,0,.332-5.161" transform="translate(-75.08 -75.262)" fill="#44d860" fill-rule="evenodd"/>
|
||||
<path id="Path_45" data-name="Path 45" d="M153,113.389h15.584V103H153Z" transform="translate(-75.08 -58.444)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_46" data-name="Path 46" d="M183.389,108.944a1.3,1.3,0,1,0,0-2.6,1.336,1.336,0,0,0-.166.017c-.01-.039-.019-.078-.03-.117a1.3,1.3,0,0,0-.5-2.5,1.285,1.285,0,0,0-.783.269q-.043-.044-.087-.087a1.285,1.285,0,0,0,.263-.776,1.3,1.3,0,0,0-2.493-.509,5.195,5.195,0,1,0,0,10,1.3,1.3,0,0,0,2.493-.509,1.285,1.285,0,0,0-.263-.776q.044-.043.087-.087a1.285,1.285,0,0,0,.783.269,1.3,1.3,0,0,0,.5-2.5c.011-.038.02-.078.03-.117a1.337,1.337,0,0,0,.166.017" transform="translate(-84.691 -57.894)" fill="#44d860" fill-rule="evenodd"/>
|
||||
<path id="Path_47" data-name="Path 47" d="M52.188,48.292a1.3,1.3,0,0,1-1.3-1.3,3.9,3.9,0,0,0-7.792,0,1.3,1.3,0,1,1-2.6,0,6.493,6.493,0,0,1,12.987,0,1.3,1.3,0,0,1-1.3,1.3" transform="translate(-21.02 -28.41)" fill-rule="evenodd"/>
|
||||
<path id="Path_48" data-name="Path 48" d="M103,139.752h31.168a10.389,10.389,0,0,0,10.389-10.389V93H113.389A10.389,10.389,0,0,0,103,103.389Z" transform="translate(-51.054 -53.638)" fill="#ffff50" fill-rule="evenodd"/>
|
||||
<path id="Path_49" data-name="Path 49" d="M141.1,94.017H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0,10.389H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0,10.389H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0-25.877H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0,10.293H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0,10.389H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m7.782-47.993c-.006,0-.011,0-.018,0-1.605.055-2.365,1.66-3.035,3.077-.7,1.48-1.24,2.443-2.126,2.414-.981-.035-1.542-1.144-2.137-2.317-.683-1.347-1.462-2.876-3.1-2.819-1.582.054-2.344,1.451-3.017,2.684-.715,1.313-1.2,2.112-2.141,2.075-1-.036-1.533-.938-2.149-1.981-.686-1.162-1.479-2.467-3.084-2.423-1.555.053-2.319,1.239-2.994,2.286-.713,1.106-1.213,1.781-2.164,1.741-1.025-.036-1.554-.784-2.167-1.65-.688-.973-1.463-2.074-3.062-2.021a3.815,3.815,0,0,0-2.959,1.879c-.64.812-1.14,1.456-2.2,1.415a.52.52,0,0,0-.037,1.039,3.588,3.588,0,0,0,3.05-1.811c.611-.777,1.139-1.448,2.178-1.483,1-.043,1.47.579,2.179,1.582.674.953,1.438,2.033,2.977,2.089,1.612.054,2.387-1.151,3.074-2.217.614-.953,1.144-1.775,2.156-1.81.931-.035,1.438.7,2.153,1.912.674,1.141,1.437,2.434,3.006,2.491,1.623.056,2.407-1.361,3.09-2.616.592-1.085,1.15-2.109,2.14-2.143.931-.022,1.417.829,2.135,2.249.671,1.326,1.432,2.828,3.026,2.886l.088,0c1.592,0,2.347-1.6,3.015-3.01.592-1.252,1.152-2.431,2.113-2.479Z" transform="translate(-55.378 -38.552)" fill-rule="evenodd"/>
|
||||
<path id="Path_50" data-name="Path 50" d="M83,163.779h20.779V143H83Z" transform="translate(-41.443 -77.665)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<g id="Group_8" data-name="Group 8" transform="matrix(0.966, -0.259, 0.259, 0.966, 51.971, 43.3)">
|
||||
<rect id="Rectangle_3" data-name="Rectangle 3" width="43.906" height="17.333" rx="2" transform="translate(0 0)" fill="#d8d8d8"/>
|
||||
<g id="Group_2" data-name="Group 2" transform="translate(0.728 10.948)">
|
||||
<rect id="Rectangle_4" data-name="Rectangle 4" width="2.537" height="2.537" rx="1" transform="translate(7.985 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_5" data-name="Rectangle 5" width="2.537" height="2.537" rx="1" transform="translate(10.991 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_6" data-name="Rectangle 6" width="2.537" height="2.537" rx="1" transform="translate(13.997 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_7" data-name="Rectangle 7" width="2.537" height="2.537" rx="1" transform="translate(17.003 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_8" data-name="Rectangle 8" width="2.537" height="2.537" rx="1" transform="translate(20.009 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_9" data-name="Rectangle 9" width="2.537" height="2.537" rx="1" transform="translate(23.015 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_10" data-name="Rectangle 10" width="2.537" height="2.537" rx="1" transform="translate(26.021 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_11" data-name="Rectangle 11" width="2.537" height="2.537" rx="1" transform="translate(29.028 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_12" data-name="Rectangle 12" width="2.537" height="2.537" rx="1" transform="translate(32.034 0)" fill="#4a4a4a"/>
|
||||
<path id="Path_51" data-name="Path 51" d="M.519,0H6.9A.519.519,0,0,1,7.421.52v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.519A.519.519,0,0,1,.519,0ZM35.653,0h6.383a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H35.652a.519.519,0,0,1-.519-.519V.519A.519.519,0,0,1,35.652,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<g id="Group_3" data-name="Group 3" transform="translate(0.728 4.878)">
|
||||
<path id="Path_52" data-name="Path 52" d="M.519,0H2.956a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.519A.519.519,0,0,1,.519,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
<rect id="Rectangle_13" data-name="Rectangle 13" width="2.537" height="2.537" rx="1" transform="translate(3.945 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_14" data-name="Rectangle 14" width="2.537" height="2.537" rx="1" transform="translate(6.951 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_15" data-name="Rectangle 15" width="2.537" height="2.537" rx="1" transform="translate(9.958 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_16" data-name="Rectangle 16" width="2.537" height="2.537" rx="1" transform="translate(12.964 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_17" data-name="Rectangle 17" width="2.537" height="2.537" rx="1" transform="translate(15.97 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_18" data-name="Rectangle 18" width="2.537" height="2.537" rx="1" transform="translate(18.976 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_19" data-name="Rectangle 19" width="2.537" height="2.537" rx="1" transform="translate(21.982 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_20" data-name="Rectangle 20" width="2.537" height="2.537" rx="1" transform="translate(24.988 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_21" data-name="Rectangle 21" width="2.537" height="2.537" rx="1" transform="translate(27.994 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_22" data-name="Rectangle 22" width="2.537" height="2.537" rx="1" transform="translate(31 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_23" data-name="Rectangle 23" width="2.537" height="2.537" rx="1" transform="translate(34.006 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_24" data-name="Rectangle 24" width="2.537" height="2.537" rx="1" transform="translate(37.012 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_25" data-name="Rectangle 25" width="2.537" height="2.537" rx="1" transform="translate(40.018 0)" fill="#4a4a4a"/>
|
||||
</g>
|
||||
<g id="Group_4" data-name="Group 4" transform="translate(43.283 4.538) rotate(180)">
|
||||
<path id="Path_53" data-name="Path 53" d="M.519,0H2.956a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.519A.519.519,0,0,1,.519,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
<rect id="Rectangle_26" data-name="Rectangle 26" width="2.537" height="2.537" rx="1" transform="translate(3.945 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_27" data-name="Rectangle 27" width="2.537" height="2.537" rx="1" transform="translate(6.951 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_28" data-name="Rectangle 28" width="2.537" height="2.537" rx="1" transform="translate(9.958 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_29" data-name="Rectangle 29" width="2.537" height="2.537" rx="1" transform="translate(12.964 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_30" data-name="Rectangle 30" width="2.537" height="2.537" rx="1" transform="translate(15.97 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_31" data-name="Rectangle 31" width="2.537" height="2.537" rx="1" transform="translate(18.976 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_32" data-name="Rectangle 32" width="2.537" height="2.537" rx="1" transform="translate(21.982 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_33" data-name="Rectangle 33" width="2.537" height="2.537" rx="1" transform="translate(24.988 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_34" data-name="Rectangle 34" width="2.537" height="2.537" rx="1" transform="translate(27.994 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_35" data-name="Rectangle 35" width="2.537" height="2.537" rx="1" transform="translate(31.001 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_36" data-name="Rectangle 36" width="2.537" height="2.537" rx="1" transform="translate(34.007 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_37" data-name="Rectangle 37" width="2.537" height="2.537" rx="1" transform="translate(37.013 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_38" data-name="Rectangle 38" width="2.537" height="2.537" rx="1" transform="translate(40.018 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_39" data-name="Rectangle 39" width="2.537" height="2.537" rx="1" transform="translate(3.945 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_40" data-name="Rectangle 40" width="2.537" height="2.537" rx="1" transform="translate(6.951 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_41" data-name="Rectangle 41" width="2.537" height="2.537" rx="1" transform="translate(9.958 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_42" data-name="Rectangle 42" width="2.537" height="2.537" rx="1" transform="translate(12.964 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_43" data-name="Rectangle 43" width="2.537" height="2.537" rx="1" transform="translate(15.97 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_44" data-name="Rectangle 44" width="2.537" height="2.537" rx="1" transform="translate(18.976 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_45" data-name="Rectangle 45" width="2.537" height="2.537" rx="1" transform="translate(21.982 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_46" data-name="Rectangle 46" width="2.537" height="2.537" rx="1" transform="translate(24.988 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_47" data-name="Rectangle 47" width="2.537" height="2.537" rx="1" transform="translate(27.994 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_48" data-name="Rectangle 48" width="2.537" height="2.537" rx="1" transform="translate(31.001 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_49" data-name="Rectangle 49" width="2.537" height="2.537" rx="1" transform="translate(34.007 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_50" data-name="Rectangle 50" width="2.537" height="2.537" rx="1" transform="translate(37.013 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_51" data-name="Rectangle 51" width="2.537" height="2.537" rx="1" transform="translate(40.018 0)" fill="#4a4a4a"/>
|
||||
</g>
|
||||
<g id="Group_6" data-name="Group 6" transform="translate(0.728 7.883)">
|
||||
<path id="Path_54" data-name="Path 54" d="M.519,0h3.47a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.52A.519.519,0,0,1,.519,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
<g id="Group_5" data-name="Group 5" transform="translate(5.073 0)">
|
||||
<rect id="Rectangle_52" data-name="Rectangle 52" width="2.537" height="2.537" rx="1" transform="translate(0 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_53" data-name="Rectangle 53" width="2.537" height="2.537" rx="1" transform="translate(3.006 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_54" data-name="Rectangle 54" width="2.537" height="2.537" rx="1" transform="translate(6.012 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_55" data-name="Rectangle 55" width="2.537" height="2.537" rx="1" transform="translate(9.018 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_56" data-name="Rectangle 56" width="2.537" height="2.537" rx="1" transform="translate(12.025 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_57" data-name="Rectangle 57" width="2.537" height="2.537" rx="1" transform="translate(15.031 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_58" data-name="Rectangle 58" width="2.537" height="2.537" rx="1" transform="translate(18.037 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_59" data-name="Rectangle 59" width="2.537" height="2.537" rx="1" transform="translate(21.042 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_60" data-name="Rectangle 60" width="2.537" height="2.537" rx="1" transform="translate(24.049 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_61" data-name="Rectangle 61" width="2.537" height="2.537" rx="1" transform="translate(27.055 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_62" data-name="Rectangle 62" width="2.537" height="2.537" rx="1" transform="translate(30.061 0)" fill="#4a4a4a"/>
|
||||
</g>
|
||||
<path id="Path_55" data-name="Path 55" d="M.52,0H3.8a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.52A.519.519,0,0,1,.519,0Z" transform="translate(38.234 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<g id="Group_7" data-name="Group 7" transform="translate(0.728 14.084)">
|
||||
<rect id="Rectangle_63" data-name="Rectangle 63" width="2.537" height="2.537" rx="1" transform="translate(0 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_64" data-name="Rectangle 64" width="2.537" height="2.537" rx="1" transform="translate(3.006 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_65" data-name="Rectangle 65" width="2.537" height="2.537" rx="1" transform="translate(6.012 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_66" data-name="Rectangle 66" width="2.537" height="2.537" rx="1" transform="translate(9.018 0)" fill="#4a4a4a"/>
|
||||
<path id="Path_56" data-name="Path 56" d="M.519,0H14.981A.519.519,0,0,1,15.5.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.018V.519A.519.519,0,0,1,.519,0Zm15.97,0h1.874a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H16.489a.519.519,0,0,1-.519-.519V.519A.519.519,0,0,1,16.489,0Z" transform="translate(12.024 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
<rect id="Rectangle_67" data-name="Rectangle 67" width="2.537" height="2.537" rx="1" transform="translate(31.376 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_68" data-name="Rectangle 68" width="2.537" height="2.537" rx="1" transform="translate(34.382 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_69" data-name="Rectangle 69" width="2.537" height="2.537" rx="1" transform="translate(40.018 0)" fill="#4a4a4a"/>
|
||||
<path id="Path_57" data-name="Path 57" d="M2.537,0V.561a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,.561V0Z" transform="translate(39.736 1.08) rotate(180)" fill="#4a4a4a"/>
|
||||
<path id="Path_58" data-name="Path 58" d="M2.537,0V.561a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,.561V0Z" transform="translate(37.2 1.456)" fill="#4a4a4a"/>
|
||||
</g>
|
||||
<rect id="Rectangle_70" data-name="Rectangle 70" width="42.273" height="1.127" rx="0.564" transform="translate(0.915 0.556)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_71" data-name="Rectangle 71" width="2.37" height="0.752" rx="0.376" transform="translate(1.949 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_72" data-name="Rectangle 72" width="2.37" height="0.752" rx="0.376" transform="translate(5.193 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_73" data-name="Rectangle 73" width="2.37" height="0.752" rx="0.376" transform="translate(7.688 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_74" data-name="Rectangle 74" width="2.37" height="0.752" rx="0.376" transform="translate(10.183 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_75" data-name="Rectangle 75" width="2.37" height="0.752" rx="0.376" transform="translate(12.679 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_76" data-name="Rectangle 76" width="2.37" height="0.752" rx="0.376" transform="translate(15.797 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_77" data-name="Rectangle 77" width="2.37" height="0.752" rx="0.376" transform="translate(18.292 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_78" data-name="Rectangle 78" width="2.37" height="0.752" rx="0.376" transform="translate(20.788 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_79" data-name="Rectangle 79" width="2.37" height="0.752" rx="0.376" transform="translate(23.283 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_80" data-name="Rectangle 80" width="2.37" height="0.752" rx="0.376" transform="translate(26.402 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_81" data-name="Rectangle 81" width="2.37" height="0.752" rx="0.376" transform="translate(28.897 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_82" data-name="Rectangle 82" width="2.37" height="0.752" rx="0.376" transform="translate(31.393 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_83" data-name="Rectangle 83" width="2.37" height="0.752" rx="0.376" transform="translate(34.512 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_84" data-name="Rectangle 84" width="2.37" height="0.752" rx="0.376" transform="translate(37.007 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_85" data-name="Rectangle 85" width="2.37" height="0.752" rx="0.376" transform="translate(39.502 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
</g>
|
||||
<path id="Path_59" data-name="Path 59" d="M123.779,148.389a2.583,2.583,0,0,0-.332.033c-.02-.078-.038-.156-.06-.234a2.594,2.594,0,1,0-2.567-4.455q-.086-.088-.174-.175a2.593,2.593,0,1,0-4.461-2.569c-.077-.022-.154-.04-.231-.06a2.6,2.6,0,1,0-5.128,0c-.077.02-.154.038-.231.06a2.594,2.594,0,1,0-4.461,2.569,10.384,10.384,0,1,0,17.314,9.992,2.592,2.592,0,1,0,.332-5.161" transform="translate(-51.054 -75.262)" fill="#44d860" fill-rule="evenodd"/>
|
||||
<path id="Path_60" data-name="Path 60" d="M83,113.389h20.779V103H83Z" transform="translate(-41.443 -58.444)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_61" data-name="Path 61" d="M123.389,108.944a1.3,1.3,0,1,0,0-2.6,1.338,1.338,0,0,0-.166.017c-.01-.039-.019-.078-.03-.117a1.3,1.3,0,0,0-.5-2.5,1.285,1.285,0,0,0-.783.269q-.043-.044-.087-.087a1.285,1.285,0,0,0,.263-.776,1.3,1.3,0,0,0-2.493-.509,5.195,5.195,0,1,0,0,10,1.3,1.3,0,0,0,2.493-.509,1.285,1.285,0,0,0-.263-.776q.044-.043.087-.087a1.285,1.285,0,0,0,.783.269,1.3,1.3,0,0,0,.5-2.5c.011-.038.02-.078.03-.117a1.335,1.335,0,0,0,.166.017" transform="translate(-55.859 -57.894)" fill="#44d860" fill-rule="evenodd"/>
|
||||
<path id="Path_62" data-name="Path 62" d="M141.8,38.745a1.41,1.41,0,0,1-.255-.026,1.309,1.309,0,0,1-.244-.073,1.349,1.349,0,0,1-.224-.119,1.967,1.967,0,0,1-.2-.161,1.52,1.52,0,0,1-.161-.2,1.282,1.282,0,0,1-.218-.722,1.41,1.41,0,0,1,.026-.255,1.5,1.5,0,0,1,.072-.244,1.364,1.364,0,0,1,.12-.223,1.252,1.252,0,0,1,.358-.358,1.349,1.349,0,0,1,.224-.119,1.309,1.309,0,0,1,.244-.073,1.2,1.2,0,0,1,.509,0,1.262,1.262,0,0,1,.468.192,1.968,1.968,0,0,1,.2.161,1.908,1.908,0,0,1,.161.2,1.322,1.322,0,0,1,.12.223,1.361,1.361,0,0,1,.1.5,1.317,1.317,0,0,1-.379.919,1.968,1.968,0,0,1-.2.161,1.346,1.346,0,0,1-.223.119,1.332,1.332,0,0,1-.5.1m10.389-.649a1.326,1.326,0,0,1-.92-.379,1.979,1.979,0,0,1-.161-.2,1.282,1.282,0,0,1-.218-.722,1.326,1.326,0,0,1,.379-.919,1.967,1.967,0,0,1,.2-.161,1.351,1.351,0,0,1,.224-.119,1.308,1.308,0,0,1,.244-.073,1.2,1.2,0,0,1,.509,0,1.262,1.262,0,0,1,.468.192,1.967,1.967,0,0,1,.2.161,1.326,1.326,0,0,1,.379.919,1.461,1.461,0,0,1-.026.255,1.323,1.323,0,0,1-.073.244,1.847,1.847,0,0,1-.119.223,1.911,1.911,0,0,1-.161.2,1.967,1.967,0,0,1-.2.161,1.294,1.294,0,0,1-.722.218" transform="translate(-69.074 -26.006)" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<g id="React-icon" transform="translate(906.3 541.56)">
|
||||
<path id="Path_330" data-name="Path 330" d="M263.668,117.179c0-5.827-7.3-11.35-18.487-14.775,2.582-11.4,1.434-20.477-3.622-23.382a7.861,7.861,0,0,0-4.016-1v4a4.152,4.152,0,0,1,2.044.466c2.439,1.4,3.5,6.724,2.672,13.574-.2,1.685-.52,3.461-.914,5.272a86.9,86.9,0,0,0-11.386-1.954,87.469,87.469,0,0,0-7.459-8.965c5.845-5.433,11.332-8.41,15.062-8.41V78h0c-4.931,0-11.386,3.514-17.913,9.611-6.527-6.061-12.982-9.539-17.913-9.539v4c3.712,0,9.216,2.959,15.062,8.356a84.687,84.687,0,0,0-7.405,8.947,83.732,83.732,0,0,0-11.4,1.972c-.412-1.793-.717-3.532-.932-5.2-.843-6.85.2-12.175,2.618-13.592a3.991,3.991,0,0,1,2.062-.466v-4h0a8,8,0,0,0-4.052,1c-5.039,2.9-6.168,11.96-3.568,23.328-11.153,3.443-18.415,8.947-18.415,14.757,0,5.828,7.3,11.35,18.487,14.775-2.582,11.4-1.434,20.477,3.622,23.382a7.882,7.882,0,0,0,4.034,1c4.931,0,11.386-3.514,17.913-9.611,6.527,6.061,12.982,9.539,17.913,9.539a8,8,0,0,0,4.052-1c5.039-2.9,6.168-11.96,3.568-23.328C256.406,128.511,263.668,122.988,263.668,117.179Zm-23.346-11.96c-.663,2.313-1.488,4.7-2.421,7.083-.735-1.434-1.506-2.869-2.349-4.3-.825-1.434-1.7-2.833-2.582-4.2C235.517,104.179,237.974,104.645,240.323,105.219Zm-8.212,19.1c-1.4,2.421-2.833,4.716-4.321,6.85-2.672.233-5.379.359-8.1.359-2.708,0-5.415-.126-8.069-.341q-2.232-3.2-4.339-6.814-2.044-3.523-3.73-7.136c1.112-2.4,2.367-4.805,3.712-7.154,1.4-2.421,2.833-4.716,4.321-6.85,2.672-.233,5.379-.359,8.1-.359,2.708,0,5.415.126,8.069.341q2.232,3.2,4.339,6.814,2.044,3.523,3.73,7.136C234.692,119.564,233.455,121.966,232.11,124.315Zm5.792-2.331c.968,2.4,1.793,4.805,2.474,7.136-2.349.574-4.823,1.058-7.387,1.434.879-1.381,1.757-2.8,2.582-4.25C236.4,124.871,237.167,123.419,237.9,121.984ZM219.72,141.116a73.921,73.921,0,0,1-4.985-5.738c1.614.072,3.263.126,4.931.126,1.685,0,3.353-.036,4.985-.126A69.993,69.993,0,0,1,219.72,141.116ZM206.38,130.555c-2.546-.377-5-.843-7.352-1.417.663-2.313,1.488-4.7,2.421-7.083.735,1.434,1.506,2.869,2.349,4.3S205.5,129.192,206.38,130.555ZM219.63,93.241a73.924,73.924,0,0,1,4.985,5.738c-1.614-.072-3.263-.126-4.931-.126-1.686,0-3.353.036-4.985.126A69.993,69.993,0,0,1,219.63,93.241ZM206.362,103.8c-.879,1.381-1.757,2.8-2.582,4.25-.825,1.434-1.6,2.869-2.331,4.3-.968-2.4-1.793-4.805-2.474-7.136C201.323,104.663,203.8,104.179,206.362,103.8Zm-16.227,22.449c-6.348-2.708-10.454-6.258-10.454-9.073s4.106-6.383,10.454-9.073c1.542-.663,3.228-1.255,4.967-1.811a86.122,86.122,0,0,0,4.034,10.92,84.9,84.9,0,0,0-3.981,10.866C193.38,127.525,191.694,126.915,190.134,126.252Zm9.647,25.623c-2.439-1.4-3.5-6.724-2.672-13.574.2-1.686.52-3.461.914-5.272a86.9,86.9,0,0,0,11.386,1.954,87.465,87.465,0,0,0,7.459,8.965c-5.845,5.433-11.332,8.41-15.062,8.41A4.279,4.279,0,0,1,199.781,151.875Zm42.532-13.663c.843,6.85-.2,12.175-2.618,13.592a3.99,3.99,0,0,1-2.062.466c-3.712,0-9.216-2.959-15.062-8.356a84.689,84.689,0,0,0,7.405-8.947,83.731,83.731,0,0,0,11.4-1.972A50.194,50.194,0,0,1,242.313,138.212Zm6.9-11.96c-1.542.663-3.228,1.255-4.967,1.811a86.12,86.12,0,0,0-4.034-10.92,84.9,84.9,0,0,0,3.981-10.866c1.775.556,3.461,1.165,5.039,1.829,6.348,2.708,10.454,6.258,10.454,9.073C259.67,119.994,255.564,123.562,249.216,126.252Z" fill="#61dafb"/>
|
||||
<path id="Path_331" data-name="Path 331" d="M320.8,78.4Z" transform="translate(-119.082 -0.328)" fill="#61dafb"/>
|
||||
<circle id="Ellipse_112" data-name="Ellipse 112" cx="8.194" cy="8.194" r="8.194" transform="translate(211.472 108.984)" fill="#61dafb"/>
|
||||
<path id="Path_332" data-name="Path 332" d="M520.5,78.1Z" transform="translate(-282.975 -0.082)" fill="#61dafb"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 35 KiB |
40
apps/docs/static/img/undraw_docusaurus_tree.svg
vendored
@ -1,40 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1129" height="663" viewBox="0 0 1129 663">
|
||||
<title>Focus on What Matters</title>
|
||||
<circle cx="321" cy="321" r="321" fill="#f2f2f2" />
|
||||
<ellipse cx="559" cy="635.49998" rx="514" ry="27.50002" fill="#3f3d56" />
|
||||
<ellipse cx="558" cy="627" rx="460" ry="22" opacity="0.2" />
|
||||
<rect x="131" y="152.5" width="840" height="50" fill="#3f3d56" />
|
||||
<path d="M166.5,727.3299A21.67009,21.67009,0,0,0,188.1701,749H984.8299A21.67009,21.67009,0,0,0,1006.5,727.3299V296h-840Z" transform="translate(-35.5 -118.5)" fill="#3f3d56" />
|
||||
<path d="M984.8299,236H188.1701A21.67009,21.67009,0,0,0,166.5,257.6701V296h840V257.6701A21.67009,21.67009,0,0,0,984.8299,236Z" transform="translate(-35.5 -118.5)" fill="#3f3d56" />
|
||||
<path d="M984.8299,236H188.1701A21.67009,21.67009,0,0,0,166.5,257.6701V296h840V257.6701A21.67009,21.67009,0,0,0,984.8299,236Z" transform="translate(-35.5 -118.5)" opacity="0.2" />
|
||||
<circle cx="181" cy="147.5" r="13" fill="#3f3d56" />
|
||||
<circle cx="217" cy="147.5" r="13" fill="#3f3d56" />
|
||||
<circle cx="253" cy="147.5" r="13" fill="#3f3d56" />
|
||||
<rect x="168" y="213.5" width="337" height="386" rx="5.33505" fill="#606060" />
|
||||
<rect x="603" y="272.5" width="284" height="22" rx="5.47638" fill="#2e8555" />
|
||||
<rect x="537" y="352.5" width="416" height="15" rx="5.47638" fill="#2e8555" />
|
||||
<rect x="537" y="396.5" width="416" height="15" rx="5.47638" fill="#2e8555" />
|
||||
<rect x="537" y="440.5" width="416" height="15" rx="5.47638" fill="#2e8555" />
|
||||
<rect x="537" y="484.5" width="416" height="15" rx="5.47638" fill="#2e8555" />
|
||||
<rect x="865" y="552.5" width="88" height="26" rx="7.02756" fill="#3ecc5f" />
|
||||
<path d="M1088.60287,624.61594a30.11371,30.11371,0,0,0,3.98291-15.266c0-13.79652-8.54358-24.98081-19.08256-24.98081s-19.08256,11.18429-19.08256,24.98081a30.11411,30.11411,0,0,0,3.98291,15.266,31.248,31.248,0,0,0,0,30.53213,31.248,31.248,0,0,0,0,30.53208,31.248,31.248,0,0,0,0,30.53208,30.11408,30.11408,0,0,0-3.98291,15.266c0,13.79652,8.54353,24.98081,19.08256,24.98081s19.08256-11.18429,19.08256-24.98081a30.11368,30.11368,0,0,0-3.98291-15.266,31.248,31.248,0,0,0,0-30.53208,31.248,31.248,0,0,0,0-30.53208,31.248,31.248,0,0,0,0-30.53213Z" transform="translate(-35.5 -118.5)" fill="#3f3d56" />
|
||||
<ellipse cx="1038.00321" cy="460.31783" rx="19.08256" ry="24.9808" fill="#3f3d56" />
|
||||
<ellipse cx="1038.00321" cy="429.78574" rx="19.08256" ry="24.9808" fill="#3f3d56" />
|
||||
<path d="M1144.93871,339.34489a91.61081,91.61081,0,0,0,7.10658-10.46092l-50.141-8.23491,54.22885.4033a91.566,91.566,0,0,0,1.74556-72.42605l-72.75449,37.74139,67.09658-49.32086a91.41255,91.41255,0,1,0-150.971,102.29805,91.45842,91.45842,0,0,0-10.42451,16.66946l65.0866,33.81447-69.40046-23.292a91.46011,91.46011,0,0,0,14.73837,85.83669,91.40575,91.40575,0,1,0,143.68892,0,91.41808,91.41808,0,0,0,0-113.02862Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
|
||||
<path d="M981.6885,395.8592a91.01343,91.01343,0,0,0,19.56129,56.51431,91.40575,91.40575,0,1,0,143.68892,0C1157.18982,436.82067,981.6885,385.60008,981.6885,395.8592Z" transform="translate(-35.5 -118.5)" opacity="0.1" />
|
||||
<path d="M365.62,461.43628H477.094v45.12043H365.62Z" transform="translate(-35.5 -118.5)" fill="#fff" fill-rule="evenodd" />
|
||||
<path d="M264.76252,608.74122a26.50931,26.50931,0,0,1-22.96231-13.27072,26.50976,26.50976,0,0,0,22.96231,39.81215H291.304V608.74122Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
|
||||
<path d="M384.17242,468.57061l92.92155-5.80726V449.49263a26.54091,26.54091,0,0,0-26.54143-26.54143H331.1161l-3.31768-5.74622a3.83043,3.83043,0,0,0-6.63536,0l-3.31768,5.74622-3.31767-5.74622a3.83043,3.83043,0,0,0-6.63536,0l-3.31768,5.74622L301.257,417.205a3.83043,3.83043,0,0,0-6.63536,0L291.304,422.9512c-.02919,0-.05573.004-.08625.004l-5.49674-5.49541a3.8293,3.8293,0,0,0-6.4071,1.71723l-1.81676,6.77338L270.607,424.1031a3.82993,3.82993,0,0,0-4.6912,4.69253l1.84463,6.89148-6.77072,1.81411a3.8315,3.8315,0,0,0-1.71988,6.40975l5.49673,5.49673c0,.02787-.004.05574-.004.08493l-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74621,3.31768L259.0163,466.081a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31767a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31767a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31768a3.83042,3.83042,0,0,0,0,6.63535l5.74622,3.31768-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768L259.0163,558.976a3.83042,3.83042,0,0,0,0,6.63535l5.74622,3.31768-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31768a3.83042,3.83042,0,0,0,0,6.63535l5.74622,3.31768-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768A26.54091,26.54091,0,0,0,291.304,635.28265H450.55254A26.5409,26.5409,0,0,0,477.094,608.74122V502.5755l-92.92155-5.80727a14.12639,14.12639,0,0,1,0-28.19762" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
|
||||
<path d="M424.01111,635.28265h39.81214V582.19979H424.01111Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
|
||||
<path d="M490.36468,602.10586a6.60242,6.60242,0,0,0-.848.08493c-.05042-.19906-.09821-.39945-.15393-.59852A6.62668,6.62668,0,1,0,482.80568,590.21q-.2203-.22491-.44457-.44589a6.62391,6.62391,0,1,0-11.39689-6.56369c-.1964-.05575-.39414-.10218-.59056-.15262a6.63957,6.63957,0,1,0-13.10086,0c-.1964.05042-.39414.09687-.59056.15262a6.62767,6.62767,0,1,0-11.39688,6.56369,26.52754,26.52754,0,1,0,44.23127,25.52756,6.6211,6.6211,0,1,0,.848-13.18579" transform="translate(-35.5 -118.5)" fill="#44d860" fill-rule="evenodd" />
|
||||
<path d="M437.28182,555.65836H477.094V529.11693H437.28182Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
|
||||
<path d="M490.36468,545.70532a3.31768,3.31768,0,0,0,0-6.63536,3.41133,3.41133,0,0,0-.42333.04247c-.02655-.09953-.04911-.19907-.077-.29859a3.319,3.319,0,0,0-1.278-6.37923,3.28174,3.28174,0,0,0-2.00122.68742q-.10947-.11346-.22294-.22295a3.282,3.282,0,0,0,.67149-1.98265,3.31768,3.31768,0,0,0-6.37-1.2992,13.27078,13.27078,0,1,0,0,25.54082,3.31768,3.31768,0,0,0,6.37-1.2992,3.282,3.282,0,0,0-.67149-1.98265q.11347-.10947.22294-.22294a3.28174,3.28174,0,0,0,2.00122.68742,3.31768,3.31768,0,0,0,1.278-6.37923c.02786-.0982.05042-.19907.077-.29859a3.41325,3.41325,0,0,0,.42333.04246" transform="translate(-35.5 -118.5)" fill="#44d860" fill-rule="evenodd" />
|
||||
<path d="M317.84538,466.081a3.31768,3.31768,0,0,1-3.31767-3.31768,9.953,9.953,0,1,0-19.90608,0,3.31768,3.31768,0,1,1-6.63535,0,16.58839,16.58839,0,1,1,33.17678,0,3.31768,3.31768,0,0,1-3.31768,3.31768" transform="translate(-35.5 -118.5)" fill-rule="evenodd" />
|
||||
<path d="M370.92825,635.28265h79.62429A26.5409,26.5409,0,0,0,477.094,608.74122v-92.895H397.46968a26.54091,26.54091,0,0,0-26.54143,26.54143Z" transform="translate(-35.5 -118.5)" fill="#ffff50" fill-rule="evenodd" />
|
||||
<path d="M457.21444,556.98543H390.80778a1.32707,1.32707,0,0,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414m0,26.54143H390.80778a1.32707,1.32707,0,1,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414m0,26.54143H390.80778a1.32707,1.32707,0,1,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414m0-66.10674H390.80778a1.32707,1.32707,0,0,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414m0,26.29459H390.80778a1.32707,1.32707,0,0,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414m0,26.54143H390.80778a1.32707,1.32707,0,0,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414M477.094,474.19076c-.01592,0-.0292-.008-.04512-.00663-4.10064.13934-6.04083,4.24132-7.75274,7.86024-1.78623,3.78215-3.16771,6.24122-5.43171,6.16691-2.50685-.09024-3.94007-2.92222-5.45825-5.91874-1.74377-3.44243-3.73438-7.34667-7.91333-7.20069-4.04227.138-5.98907,3.70784-7.70631,6.857-1.82738,3.35484-3.07084,5.39455-5.46887,5.30033-2.55727-.09289-3.91619-2.39536-5.48877-5.06013-1.75306-2.96733-3.77951-6.30359-7.8775-6.18946-3.97326.13669-5.92537,3.16507-7.64791,5.83912-1.82207,2.82666-3.09872,4.5492-5.52725,4.447-2.61832-.09289-3.9706-2.00388-5.53522-4.21611-1.757-2.4856-3.737-5.299-7.82308-5.16231-3.88567.13271-5.83779,2.61434-7.559,4.80135-1.635,2.07555-2.9116,3.71846-5.61218,3.615a1.32793,1.32793,0,1,0-.09555,2.65414c4.00377.134,6.03154-2.38873,7.79257-4.6275,1.562-1.9853,2.91027-3.69855,5.56441-3.78879,2.55594-.10882,3.75429,1.47968,5.56707,4.04093,1.7212,2.43385,3.67465,5.19416,7.60545,5.33616,4.11789.138,6.09921-2.93946,7.8536-5.66261,1.56861-2.43385,2.92221-4.53461,5.50734-4.62352,2.37944-.08892,3.67466,1.79154,5.50072,4.885,1.72121,2.91557,3.67069,6.21865,7.67977,6.36463,4.14709.14332,6.14965-3.47693,7.89475-6.68181,1.51155-2.77092,2.93814-5.38791,5.46621-5.4755,2.37944-.05573,3.62025,2.11668,5.45558,5.74622,1.71459,3.388,3.65875,7.22591,7.73019,7.37321l.22429.004c4.06614,0,5.99571-4.08074,7.70364-7.68905,1.51154-3.19825,2.94211-6.21069,5.3972-6.33411Z" transform="translate(-35.5 -118.5)" fill-rule="evenodd" />
|
||||
<path d="M344.38682,635.28265h53.08286V582.19979H344.38682Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
|
||||
<path d="M424.01111,602.10586a6.60242,6.60242,0,0,0-.848.08493c-.05042-.19906-.09821-.39945-.15394-.59852A6.62667,6.62667,0,1,0,416.45211,590.21q-.2203-.22491-.44458-.44589a6.62391,6.62391,0,1,0-11.39689-6.56369c-.1964-.05575-.39413-.10218-.59054-.15262a6.63957,6.63957,0,1,0-13.10084,0c-.19641.05042-.39414.09687-.59055.15262a6.62767,6.62767,0,1,0-11.39689,6.56369,26.52755,26.52755,0,1,0,44.2313,25.52756,6.6211,6.6211,0,1,0,.848-13.18579" transform="translate(-35.5 -118.5)" fill="#44d860" fill-rule="evenodd" />
|
||||
<path d="M344.38682,555.65836h53.08286V529.11693H344.38682Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
|
||||
<path d="M410.74039,545.70532a3.31768,3.31768,0,1,0,0-6.63536,3.41133,3.41133,0,0,0-.42333.04247c-.02655-.09953-.04911-.19907-.077-.29859a3.319,3.319,0,0,0-1.278-6.37923,3.28174,3.28174,0,0,0-2.00122.68742q-.10947-.11346-.22294-.22295a3.282,3.282,0,0,0,.67149-1.98265,3.31768,3.31768,0,0,0-6.37-1.2992,13.27078,13.27078,0,1,0,0,25.54082,3.31768,3.31768,0,0,0,6.37-1.2992,3.282,3.282,0,0,0-.67149-1.98265q.11347-.10947.22294-.22294a3.28174,3.28174,0,0,0,2.00122.68742,3.31768,3.31768,0,0,0,1.278-6.37923c.02786-.0982.05042-.19907.077-.29859a3.41325,3.41325,0,0,0,.42333.04246" transform="translate(-35.5 -118.5)" fill="#44d860" fill-rule="evenodd" />
|
||||
<path d="M424.01111,447.8338a3.60349,3.60349,0,0,1-.65028-.06636,3.34415,3.34415,0,0,1-.62372-.18579,3.44679,3.44679,0,0,1-.572-.30522,5.02708,5.02708,0,0,1-.50429-.4114,3.88726,3.88726,0,0,1-.41007-.50428,3.27532,3.27532,0,0,1-.55737-1.84463,3.60248,3.60248,0,0,1,.06636-.65027,3.82638,3.82638,0,0,1,.18447-.62373,3.48858,3.48858,0,0,1,.30656-.57064,3.197,3.197,0,0,1,.91436-.91568,3.44685,3.44685,0,0,1,.572-.30523,3.344,3.344,0,0,1,.62372-.18578,3.06907,3.06907,0,0,1,1.30053,0,3.22332,3.22332,0,0,1,1.19436.491,5.02835,5.02835,0,0,1,.50429.41139,4.8801,4.8801,0,0,1,.41139.50429,3.38246,3.38246,0,0,1,.30522.57064,3.47806,3.47806,0,0,1,.25215,1.274A3.36394,3.36394,0,0,1,426.36,446.865a5.02708,5.02708,0,0,1-.50429.4114,3.3057,3.3057,0,0,1-1.84463.55737m26.54143-1.65884a3.38754,3.38754,0,0,1-2.35024-.96877,5.04185,5.04185,0,0,1-.41007-.50428,3.27532,3.27532,0,0,1-.55737-1.84463,3.38659,3.38659,0,0,1,.96744-2.34892,5.02559,5.02559,0,0,1,.50429-.41139,3.44685,3.44685,0,0,1,.572-.30523,3.3432,3.3432,0,0,1,.62373-.18579,3.06952,3.06952,0,0,1,1.30052,0,3.22356,3.22356,0,0,1,1.19436.491,5.02559,5.02559,0,0,1,.50429.41139,3.38792,3.38792,0,0,1,.96876,2.34892,3.72635,3.72635,0,0,1-.06636.65026,3.37387,3.37387,0,0,1-.18579.62373,4.71469,4.71469,0,0,1-.30522.57064,4.8801,4.8801,0,0,1-.41139.50429,5.02559,5.02559,0,0,1-.50429.41139,3.30547,3.30547,0,0,1-1.84463.55737" transform="translate(-35.5 -118.5)" fill-rule="evenodd" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 12 KiB |
@ -1,7 +0,0 @@
|
||||
{
|
||||
// This file is not used in compilation. It is here just for a nice editor experience.
|
||||
"extends": "@docusaurus/tsconfig",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "."
|
||||
}
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
# or
|
||||
bun dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||
@ -1,3 +0,0 @@
|
||||
import { authHandler } from "@/server/auth";
|
||||
|
||||
export { authHandler as GET, authHandler as POST };
|
||||
@ -1,29 +0,0 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import serverConfig from '@lifetracker/shared/config';
|
||||
import { getConnectionDetails } from '@lifetracker/db/drizzle';
|
||||
import { pgDump } from 'pg-dump-restore';
|
||||
|
||||
|
||||
const dbPath = path.join(serverConfig.dataDir, `lifetracker-${new Date().getTime()}.sql`);
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
try {
|
||||
console.log((await pgDump(getConnectionDetails(), {
|
||||
filePath: dbPath,
|
||||
})).stdout);
|
||||
// Read the production database file
|
||||
const dbFile = fs.readFileSync(dbPath);
|
||||
return new NextResponse(dbFile, {
|
||||
headers: {
|
||||
'Content-Type': 'application/octet-stream',
|
||||
'Content-Disposition': `attachment; filename="lifetracker-${new Date().getTime()}.sql"`
|
||||
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error downloading the database:', error);
|
||||
return NextResponse.json({ message: 'Error processing download.' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@ -1,72 +0,0 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import serverConfig from '@lifetracker/shared/config';
|
||||
import { pgDump, pgRestore } from 'pg-dump-restore';
|
||||
import { db, getConnectionDetails } from '@lifetracker/db/drizzle';
|
||||
import { sql } from 'drizzle-orm';
|
||||
|
||||
// Define paths for uploaded files and the production DB
|
||||
const UPLOAD_DIR = path.join(process.cwd(), 'uploads');
|
||||
const DB_PATH = path.join(serverConfig.dataDir, `lifetracker-${new Date().getTime()}.sql`);
|
||||
|
||||
// Ensure upload directory exists
|
||||
if (!fs.existsSync(UPLOAD_DIR)) {
|
||||
fs.mkdirSync(UPLOAD_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
try {
|
||||
const contentType = req.headers.get('content-type') || '';
|
||||
if (!contentType.includes('multipart/form-data')) {
|
||||
return NextResponse.json({ message: 'Invalid content type. Please upload a file.' }, { status: 400 });
|
||||
}
|
||||
|
||||
// Create a writable stream for saving the uploaded file
|
||||
const formBoundary = contentType.split('boundary=')[1];
|
||||
const formData = req.body;
|
||||
|
||||
// Parse and save the uploaded file
|
||||
const chunks: Buffer[] = [];
|
||||
for await (const chunk of formData) {
|
||||
chunks.push(chunk);
|
||||
}
|
||||
|
||||
const bodyBuffer = Buffer.concat(chunks);
|
||||
const start = bodyBuffer.indexOf(`\r\n\r\n`) + 4;
|
||||
const end = bodyBuffer.indexOf(`\r\n--${formBoundary}`, start);
|
||||
const fileContent = bodyBuffer.slice(start, end);
|
||||
|
||||
// Save the uploaded file to the upload directory
|
||||
const uploadedFilePath = path.join(UPLOAD_DIR, 'uploaded.sql');
|
||||
fs.writeFileSync(uploadedFilePath, fileContent);
|
||||
|
||||
// Back up the existing production database
|
||||
const backupPath = `${serverConfig.dataDir}/pg-backup-${new Date().getTime()}.sql`;
|
||||
const { stdout, stderr } = await pgDump(getConnectionDetails(), {
|
||||
filePath: `${backupPath}`,
|
||||
});
|
||||
|
||||
console.log("Backed up existing data to ", backupPath);
|
||||
console.log(stdout);
|
||||
|
||||
// If it's a SQL file, restore the production database from the uploaded file
|
||||
if (uploadedFilePath.endsWith('.sql')) {
|
||||
const { stdout: restoreStdout, stderr: restoreStderr } = await pgRestore(getConnectionDetails(), {
|
||||
clean: true,
|
||||
filePath: uploadedFilePath,
|
||||
});
|
||||
}
|
||||
// If it ends in .db, assume it's a sqlite
|
||||
else if (uploadedFilePath.endsWith('.db')) {
|
||||
// TODO, if ever
|
||||
return NextResponse.json({ message: 'Invalid file type. Please upload a .sql file.' }, { status: 400 });
|
||||
}
|
||||
else { return NextResponse.json({ message: 'Invalid file type. Please upload a .sql file.' }, { status: 400 }); }
|
||||
|
||||
return NextResponse.json({ message: 'Database uploaded and replaced successfully!' }, { status: 200 });
|
||||
} catch (error) {
|
||||
console.error('Error handling the uploaded file:', error);
|
||||
return NextResponse.json({ message: 'Error processing upload.' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
export const GET = async (_req: NextRequest) => {
|
||||
return NextResponse.json({
|
||||
status: "ok",
|
||||
message: "Web app is working",
|
||||
});
|
||||
};
|
||||
@ -1,21 +0,0 @@
|
||||
import { createContextFromRequest } from "@/server/api/client";
|
||||
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
|
||||
import { appRouter } from "@lifetracker/trpc/routers/_app";
|
||||
|
||||
const handler = (req: Request) =>
|
||||
fetchRequestHandler({
|
||||
endpoint: "/api/trpc",
|
||||
req,
|
||||
router: appRouter,
|
||||
onError: ({ path, error }) => {
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
console.error(`❌ tRPC failed on ${path}`);
|
||||
}
|
||||
console.error(error);
|
||||
},
|
||||
|
||||
createContext: async (opts) => {
|
||||
return await createContextFromRequest(opts.req);
|
||||
},
|
||||
});
|
||||
export { handler as GET, handler as POST };
|
||||
@ -1,42 +0,0 @@
|
||||
import { NextRequest } from "next/server";
|
||||
import { zMeasurementInputSchema } from "@lifetracker/shared/types/metrics";
|
||||
|
||||
import { buildHandler } from "../utils/handler";
|
||||
|
||||
import spacetime from "spacetime";
|
||||
|
||||
export const POST = (req: NextRequest) =>
|
||||
buildHandler({
|
||||
req,
|
||||
bodySchema: zMeasurementInputSchema,
|
||||
handler: async ({ api, body }) => {
|
||||
|
||||
const datetime = spacetime(
|
||||
body?.dateTimeQuery || new Date(),
|
||||
body?.timezone || "Etc/UTC"
|
||||
).goto("Etc/UTC");
|
||||
|
||||
// const dayId = (await api.days.get({
|
||||
// dateQuery: datetime.format("iso-short")
|
||||
// })).id;
|
||||
|
||||
const hour = await api.hours.get({
|
||||
dateQuery: spacetime(datetime).startOf("hour").format("iso"),
|
||||
});
|
||||
|
||||
const obj = {
|
||||
metricName: body!.metricName,
|
||||
hourId: hour.id!,
|
||||
dayId: hour.dayId
|
||||
};
|
||||
|
||||
const measurement =
|
||||
body?.value
|
||||
? await api.measurements.setValue({
|
||||
...obj,
|
||||
value: body.value!,
|
||||
})
|
||||
: await api.measurements.incrementCount(obj);
|
||||
return { status: 201, resp: measurement };
|
||||
},
|
||||
});
|
||||
@ -1,170 +0,0 @@
|
||||
import { NextRequest } from "next/server";
|
||||
import {
|
||||
createContextFromRequest,
|
||||
createTrcpClientFromCtx,
|
||||
} from "@/server/api/client";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { z, ZodError } from "zod";
|
||||
|
||||
import { Context } from "@lifetracker/trpc";
|
||||
|
||||
function trpcCodeToHttpCode(code: TRPCError["code"]) {
|
||||
switch (code) {
|
||||
case "BAD_REQUEST":
|
||||
case "PARSE_ERROR":
|
||||
return 400;
|
||||
case "UNAUTHORIZED":
|
||||
return 401;
|
||||
case "FORBIDDEN":
|
||||
return 403;
|
||||
case "NOT_FOUND":
|
||||
return 404;
|
||||
case "METHOD_NOT_SUPPORTED":
|
||||
return 405;
|
||||
case "TIMEOUT":
|
||||
return 408;
|
||||
case "PAYLOAD_TOO_LARGE":
|
||||
return 413;
|
||||
case "INTERNAL_SERVER_ERROR":
|
||||
return 500;
|
||||
default:
|
||||
return 500;
|
||||
}
|
||||
}
|
||||
|
||||
interface ErrorMessage {
|
||||
path: (string | number)[];
|
||||
message: string;
|
||||
}
|
||||
|
||||
function formatZodError(error: ZodError): string {
|
||||
if (!error.issues) {
|
||||
return error.message || "An unknown error occurred";
|
||||
}
|
||||
|
||||
const errors: ErrorMessage[] = error.issues.map((issue) => ({
|
||||
path: issue.path,
|
||||
message: issue.message,
|
||||
}));
|
||||
|
||||
const formattedErrors = errors.map((err) => {
|
||||
const path = err.path.join(".");
|
||||
return path ? `${path}: ${err.message}` : err.message;
|
||||
});
|
||||
|
||||
return `${formattedErrors.join(", ")}`;
|
||||
}
|
||||
|
||||
export interface TrpcAPIRequest<SearchParamsT, BodyType> {
|
||||
ctx: Context;
|
||||
api: ReturnType<typeof createTrcpClientFromCtx>;
|
||||
searchParams: SearchParamsT extends z.ZodTypeAny
|
||||
? z.infer<SearchParamsT>
|
||||
: undefined;
|
||||
body: BodyType extends z.ZodTypeAny
|
||||
? z.infer<BodyType> | undefined
|
||||
: undefined;
|
||||
}
|
||||
|
||||
type SchemaType<T> = T extends z.ZodTypeAny
|
||||
? z.infer<T> | undefined
|
||||
: undefined;
|
||||
|
||||
export async function buildHandler<
|
||||
SearchParamsT extends z.ZodTypeAny | undefined,
|
||||
BodyT extends z.ZodTypeAny | undefined,
|
||||
InputT extends TrpcAPIRequest<SearchParamsT, BodyT>,
|
||||
>({
|
||||
req,
|
||||
handler,
|
||||
searchParamsSchema,
|
||||
bodySchema,
|
||||
}: {
|
||||
req: NextRequest;
|
||||
handler: (req: InputT) => Promise<{ status: number; resp?: object }>;
|
||||
searchParamsSchema?: SearchParamsT | undefined;
|
||||
bodySchema?: BodyT | undefined;
|
||||
}) {
|
||||
try {
|
||||
const ctx = await createContextFromRequest(req);
|
||||
const api = createTrcpClientFromCtx(ctx);
|
||||
|
||||
let searchParams: SchemaType<SearchParamsT> | undefined = undefined;
|
||||
if (searchParamsSchema !== undefined) {
|
||||
searchParams = searchParamsSchema.parse(
|
||||
Object.fromEntries(req.nextUrl.searchParams.entries()),
|
||||
) as SchemaType<SearchParamsT>;
|
||||
}
|
||||
|
||||
let body: SchemaType<BodyT> | undefined = undefined;
|
||||
if (bodySchema) {
|
||||
if (req.headers.get("Content-Type") !== "application/json") {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Content-Type must be application/json",
|
||||
});
|
||||
}
|
||||
|
||||
let bodyJson = undefined;
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
bodyJson = await req.json();
|
||||
} catch (e) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: `Invalid JSON: ${(e as Error).message}`,
|
||||
});
|
||||
}
|
||||
body = bodySchema.parse(bodyJson) as SchemaType<BodyT>;
|
||||
}
|
||||
|
||||
const { status, resp } = await handler({
|
||||
ctx,
|
||||
api,
|
||||
searchParams,
|
||||
body,
|
||||
} as InputT);
|
||||
|
||||
return new Response(resp ? JSON.stringify(resp) : null, {
|
||||
status,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof ZodError) {
|
||||
return new Response(
|
||||
JSON.stringify({ code: "ParseError", message: formatZodError(e) }),
|
||||
{
|
||||
status: 400,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
if (e instanceof TRPCError) {
|
||||
let message = e.message;
|
||||
if (e.cause instanceof ZodError) {
|
||||
message = formatZodError(e.cause);
|
||||
}
|
||||
return new Response(JSON.stringify({ code: e.code, error: message }), {
|
||||
status: trpcCodeToHttpCode(e.code),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
} else {
|
||||
const error = e as Error;
|
||||
console.error(
|
||||
`Unexpected error in: ${req.method} ${req.nextUrl.pathname}:\n${error.stack}`,
|
||||
);
|
||||
return new Response(JSON.stringify({ code: "UnknownError" }), {
|
||||
status: 500,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import {
|
||||
MAX_NUM_BOOKMARKS_PER_PAGE,
|
||||
zCursorV2,
|
||||
} from "@hoarder/shared/types/bookmarks";
|
||||
|
||||
export const zPagination = z.object({
|
||||
limit: z.coerce.number().max(MAX_NUM_BOOKMARKS_PER_PAGE).optional(),
|
||||
cursor: z
|
||||
.string()
|
||||
.refine((val) => val.includes("_"), "Must be a valid cursor")
|
||||
.transform((val) => {
|
||||
const [id, createdAt] = val.split("_");
|
||||
return { id, createdAt };
|
||||
})
|
||||
.pipe(z.object({ id: z.string(), createdAt: z.coerce.date() }))
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export function adaptPagination<
|
||||
T extends { nextCursor: z.infer<typeof zCursorV2> | null },
|
||||
>(input: T) {
|
||||
const { nextCursor, ...rest } = input;
|
||||
if (!nextCursor) {
|
||||
return input;
|
||||
}
|
||||
return {
|
||||
...rest,
|
||||
nextCursor: `${nextCursor.id}_${nextCursor.createdAt.toISOString()}`,
|
||||
};
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const zStringBool = z
|
||||
.string()
|
||||
.refine((val) => val === "true" || val === "false", "Must be true or false")
|
||||
.transform((val) => val === "true");
|
||||
|
Before Width: | Height: | Size: 3.4 KiB |
@ -1,27 +0,0 @@
|
||||
import { redirect } from "next/navigation";
|
||||
import AdminActions from "@/components/dashboard/admin/AdminActions";
|
||||
import ServerStats from "@/components/dashboard/admin/ServerStats";
|
||||
import UserList from "@/components/dashboard/admin/UserList";
|
||||
import { getServerAuthSession } from "@/server/auth";
|
||||
import DatabaseSettings from "@/components/dashboard/admin/DatabaseSettings";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
|
||||
export default async function AdminPage() {
|
||||
const session = await getServerAuthSession();
|
||||
if (!session || session.user.role !== "admin") {
|
||||
redirect("/");
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className="rounded-md border bg-background p-4">
|
||||
{/* <ServerStats /> */}
|
||||
{/* <AdminActions /> */}
|
||||
<Separator />
|
||||
<DatabaseSettings />
|
||||
</div>
|
||||
<div className="mt-4 rounded-md border bg-background p-4">
|
||||
<UserList />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
import React from "react";
|
||||
import AnalyticsView from "@/components/dashboard/analytics/AnalyticsView";
|
||||
|
||||
export default async function AnalyticsPage() {
|
||||
|
||||
|
||||
return (
|
||||
<AnalyticsView />
|
||||
);
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
import React from "react";
|
||||
import CategoriesView from "@/components/dashboard/categories/CategoriesView";
|
||||
|
||||
export default async function CategoriesPage() {
|
||||
return (
|
||||
<div>
|
||||
<CategoriesView />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
import { api } from "@/server/api/client";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import DayView from "@/components/dashboard/days/DayView";
|
||||
import { getServerAuthSession } from "@/server/auth";
|
||||
import LoadingSpinner from "@/components/ui/spinner";
|
||||
import { useTimezone } from "@/lib/userLocalSettings/client";
|
||||
import { getUserLocalSettings } from "@/lib/userLocalSettings/userLocalSettings";
|
||||
|
||||
export default async function DayPage({ params }: { params: { dateQuery: string }; }) {
|
||||
const session = await getServerAuthSession();
|
||||
if (!session) {
|
||||
redirect("/");
|
||||
}
|
||||
|
||||
let day;
|
||||
|
||||
try {
|
||||
day = await api.days.get({
|
||||
dateQuery: (await params).dateQuery,
|
||||
timezone: (await getUserLocalSettings()).timezone,
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof TRPCError) {
|
||||
if (e.code == "NOT_FOUND") {
|
||||
notFound();
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
{
|
||||
day == undefined ?
|
||||
<LoadingSpinner /> :
|
||||
<DayView
|
||||
day={day}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
export default async function MainDayPage() {
|
||||
return (
|
||||
<div>
|
||||
Hello from a logged in page!
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
"use client";
|
||||
|
||||
export default function Error() {
|
||||
return (
|
||||
<div className="flex size-full">
|
||||
<div className="m-auto text-3xl">Something went wrong</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
import Header from "@/components/dashboard/header/Header";
|
||||
import MobileSidebar from "@/components/dashboard/sidebar/MobileSidebar";
|
||||
import Sidebar from "@/components/dashboard/sidebar/Sidebar";
|
||||
import DemoModeBanner from "@/components/DemoModeBanner";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import ValidAccountCheck from "@/components/utils/ValidAccountCheck";
|
||||
import { cn } from "@/lib/utils";
|
||||
import serverConfig from "@lifetracker/shared/config";
|
||||
|
||||
export default async function Dashboard({
|
||||
children,
|
||||
modal,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
modal: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<div>
|
||||
<Header />
|
||||
<div className="flex min-h-[calc(100vh-64px)] w-screen flex-col sm:h-[calc(100vh-64px)] sm:flex-row">
|
||||
<ValidAccountCheck />
|
||||
<div className="hidden flex-none sm:flex">
|
||||
<Sidebar />
|
||||
</div>
|
||||
<main className="flex-1 bg-muted sm:overflow-y-auto">
|
||||
{serverConfig.demoMode && <DemoModeBanner />}
|
||||
<div className="block w-full sm:hidden">
|
||||
<MobileSidebar />
|
||||
<Separator />
|
||||
</div>
|
||||
{modal}
|
||||
<div className={cn("min-h-30 p-4")}>{children}</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
import MetricsView from "@/components/dashboard/metrics/MetricsView";
|
||||
|
||||
export default async function MetricsPage() {
|
||||
return (
|
||||
<>
|
||||
<MetricsView />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<div className="flex size-full">
|
||||
<div className="m-auto text-3xl">Not Found :(</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
import React from "react";
|
||||
import spacetime from "spacetime";
|
||||
import { api } from "@/server/api/client";
|
||||
import TimelineView from "@/components/dashboard/timeline/TimelineView";
|
||||
|
||||
async function fetchDays(view: string, dateQuery: string) {
|
||||
const timezone = await api.users.getTimezone();
|
||||
|
||||
const today = spacetime(dateQuery ?? "today");
|
||||
const firstDay = today.subtract(2, "week").startOf("week");
|
||||
|
||||
const days = [];
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const dayDate = firstDay.add(i, "day").format("iso-short");
|
||||
// console.log(dayDate);
|
||||
const dayRes = await api.days.get({
|
||||
dateQuery: dayDate,
|
||||
timezone: timezone,
|
||||
});
|
||||
days.push(dayRes);
|
||||
}
|
||||
|
||||
return days;
|
||||
}
|
||||
|
||||
export default async function TimelinePage({ params }: { params: { view: string, dateQuery: string }; }) {
|
||||
const { view = "month", dateQuery } = await params;
|
||||
const days = await fetchDays(view, dateQuery);
|
||||
|
||||
return (
|
||||
<>
|
||||
<TimelineView days={days} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
@ -1,76 +0,0 @@
|
||||
|
||||
import type { Metadata } from "next";
|
||||
import { Inter } from "next/font/google";
|
||||
|
||||
import "@mantine/core/styles.css";
|
||||
import "@mantine/dates/styles.css";
|
||||
import "@lifetracker/tailwind-config/globals.css";
|
||||
|
||||
import type { Viewport } from "next";
|
||||
import React from "react";
|
||||
import { cookies } from "next/headers";
|
||||
import { Toaster } from "@/components/ui/toaster";
|
||||
import Providers from "@/lib/providers";
|
||||
import {
|
||||
defaultUserLocalSettings,
|
||||
parseUserLocalSettings,
|
||||
USER_LOCAL_SETTINGS_COOKIE_NAME,
|
||||
} from "@/lib/userLocalSettings/types";
|
||||
import { getServerAuthSession } from "@/server/auth";
|
||||
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
|
||||
|
||||
import serverConfig, { clientConfig } from "@lifetracker/shared/config";
|
||||
import { useTimezone } from "@lifetracker/shared-react/hooks/timezones";
|
||||
import { getUserLocalSettings } from "@/lib/userLocalSettings/userLocalSettings";
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ["latin"],
|
||||
fallback: ["sans-serif"],
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Lifetracker",
|
||||
applicationName: "Lifetracker",
|
||||
description:
|
||||
"The all-in-one life tracking app.",
|
||||
manifest: "/manifest.json",
|
||||
appleWebApp: {
|
||||
capable: true,
|
||||
title: "LifeTracker",
|
||||
},
|
||||
formatDetection: {
|
||||
telephone: false,
|
||||
},
|
||||
};
|
||||
|
||||
export const viewport: Viewport = {
|
||||
width: "device-width",
|
||||
initialScale: 1,
|
||||
maximumScale: 1,
|
||||
userScalable: false,
|
||||
};
|
||||
|
||||
export default async function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
const session = await getServerAuthSession();
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={inter.className}>
|
||||
<Providers
|
||||
session={session}
|
||||
clientConfig={clientConfig}
|
||||
userLocalSettings={
|
||||
await getUserLocalSettings()
|
||||
}
|
||||
>
|
||||
{children}
|
||||
<ReactQueryDevtools initialIsOpen={false} />
|
||||
</Providers>
|
||||
<Toaster />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
import { redirect } from "next/navigation";
|
||||
import { getServerAuthSession } from "@/server/auth";
|
||||
|
||||
export default async function Home() {
|
||||
const session = await getServerAuthSession();
|
||||
if (session) {
|
||||
redirect("/dashboard/day/today");
|
||||
} else {
|
||||
redirect("/signin");
|
||||
}
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
import ApiKeySettings from "@/components/settings/ApiKeySettings";
|
||||
|
||||
export default async function ApiKeysPage() {
|
||||
return (
|
||||
<div className="rounded-md border bg-background p-4">
|
||||
<ApiKeySettings />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
import AppSettings from "@/components/settings/AppSettings";
|
||||
|
||||
export default async function AppSettingsPage() {
|
||||
return (
|
||||
<div className="rounded-md border bg-background p-4">
|
||||
<AppSettings />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
import ColorSettings from "@/components/settings/ColorSettings";
|
||||
|
||||
export default async function ColorsPage() {
|
||||
return (
|
||||
<div className="rounded-md border bg-background p-4">
|
||||
<ColorSettings />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
import DatabaseSettings from "@/components/dashboard/admin/DatabaseSettings";
|
||||
|
||||
export default async function DatabasePage() {
|
||||
return (
|
||||
<div className="rounded-md border bg-background p-4">
|
||||
<DatabaseSettings />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
import { ChangePassword } from "@/components/settings/ChangePassword";
|
||||
import UserDetails from "@/components/settings/UserDetails";
|
||||
|
||||
export default async function InfoPage() {
|
||||
return (
|
||||
<div className="rounded-md border bg-background p-4">
|
||||
<UserDetails />
|
||||
<ChangePassword />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,34 +0,0 @@
|
||||
import Header from "@/components/dashboard/header/Header";
|
||||
import DemoModeBanner from "@/components/DemoModeBanner";
|
||||
import MobileSidebar from "@/components/settings/sidebar/MobileSidebar";
|
||||
import Sidebar from "@/components/settings/sidebar/Sidebar";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import ValidAccountCheck from "@/components/utils/ValidAccountCheck";
|
||||
|
||||
import serverConfig from "@lifetracker/shared/config";
|
||||
|
||||
export default async function SettingsLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<div>
|
||||
<Header />
|
||||
<div className="flex min-h-[calc(100vh-64px)] w-screen flex-col sm:h-[calc(100vh-64px)] sm:flex-row">
|
||||
<ValidAccountCheck />
|
||||
<div className="hidden flex-none sm:flex">
|
||||
<Sidebar />
|
||||
</div>
|
||||
<main className="flex-1 bg-muted sm:overflow-y-auto">
|
||||
{serverConfig.demoMode && <DemoModeBanner />}
|
||||
<div className="block w-full sm:hidden">
|
||||
<MobileSidebar />
|
||||
<Separator />
|
||||
</div>
|
||||
<div className="min-h-30 container p-4">{children}</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export default function SettingsHomepage() {
|
||||
redirect("/settings/info");
|
||||
return null;
|
||||
}
|
||||