diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 00000000..402dc84b
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,24 @@
+**/.classpath
+**/.dockerignore
+**/.env
+**/.git
+**/.gitignore
+**/.project
+**/.settings
+**/.toolstarget
+**/.vs
+**/.vscode
+**/*.*proj.user
+**/*.dbmdl
+**/*.jfm
+**/charts
+**/docker-compose*
+**/compose*
+**/Dockerfile*
+**/node_modules
+**/npm-debug.log
+**/obj
+**/secrets.dev.yaml
+**/values.dev.yaml
+README.md
+config/
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..a481ff77
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,13 @@
+# EditorConfig is awesome: https://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+max_line_length = 120
diff --git a/.gitignore b/.gitignore
index c87c9b39..32e06969 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,3 +34,6 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
+
+# homepage
+/config
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 00000000..92610650
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,35 @@
+# Install dependencies only when needed
+FROM node:16-alpine AS deps
+RUN apk add --no-cache libc6-compat
+WORKDIR /app
+COPY package.json pnpm-lock.yaml* ./
+RUN yarn global add pnpm
+RUN pnpm install
+
+# Rebuild the source code only when needed
+FROM node:16-alpine AS builder
+WORKDIR /app
+COPY --from=deps /app/node_modules ./node_modules
+COPY . .
+
+RUN npm run build
+
+# Production image, copy all the files and run next
+FROM node:16-alpine AS runner
+WORKDIR /app
+ENV NODE_ENV production
+RUN addgroup --system --gid 1001 nodejs
+RUN adduser --system --uid 1001 nextjs
+COPY --from=builder /app/next.config.js ./
+COPY --from=builder /app/public ./public
+COPY --from=builder /app/package.json ./package.json
+COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
+COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
+
+# Since we run in a local environment, we need to accept self signed certificates
+ENV NODE_TLS_REJECT_UNAUTHORIZED 0
+
+USER nextjs
+EXPOSE 3000
+ENV PORT 3000
+CMD ["node", "server.js"]
diff --git a/README.md b/README.md
index b12f3e33..f5ffe734 100644
--- a/README.md
+++ b/README.md
@@ -1,34 +1,21 @@
-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:
+Install NPM packages, this project uses [pnpm](https://pnpm.io/) (and so should you!):
```bash
-npm run dev
-# or
-yarn dev
+pnpm install
```
-Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
+Start the development server:
-You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
+```bash
+pnpm dev
+```
-[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`.
+Open [http://localhost:3000](http://localhost:3000) to start.
-The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
-## Learn More
+## Configuration
-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.
+Configuration is done in the /config directory using .yaml files. Refer to each config for
+the specific configuration options.
diff --git a/jsconfig.json b/jsconfig.json
new file mode 100644
index 00000000..fd99e2ea
--- /dev/null
+++ b/jsconfig.json
@@ -0,0 +1,5 @@
+{
+ "compilerOptions": {
+ "baseUrl": "./src/",
+ }
+}
\ No newline at end of file
diff --git a/next.config.js b/next.config.js
index ae887958..9959d271 100644
--- a/next.config.js
+++ b/next.config.js
@@ -1,7 +1,11 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
+ output: "standalone",
swcMinify: true,
-}
+ images: {
+ domains: ["cdn.jsdelivr.net"],
+ },
+};
-module.exports = nextConfig
+module.exports = nextConfig;
diff --git a/package.json b/package.json
index 8717cedf..c8646bde 100644
--- a/package.json
+++ b/package.json
@@ -9,12 +9,25 @@
"lint": "next lint"
},
"dependencies": {
+ "@headlessui/react": "^1.6.6",
+ "@tailwindcss/forms": "^0.5.2",
+ "dockerode": "^3.3.3",
+ "js-yaml": "^4.1.0",
+ "memory-cache": "^0.2.0",
"next": "12.2.5",
+ "node-os-utils": "^1.3.7",
"react": "18.2.0",
- "react-dom": "18.2.0"
+ "react-dom": "18.2.0",
+ "react-icons": "^4.4.0",
+ "sharp": "^0.30.7",
+ "swr": "^1.3.0"
},
"devDependencies": {
+ "autoprefixer": "^10.4.8",
"eslint": "8.22.0",
- "eslint-config-next": "12.2.5"
+ "eslint-config-next": "12.2.5",
+ "postcss": "^8.4.16",
+ "tailwindcss": "^3.1.8",
+ "typescript": "^4.7.4"
}
}
diff --git a/pages/_app.js b/pages/_app.js
deleted file mode 100644
index 1e1cec92..00000000
--- a/pages/_app.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import '../styles/globals.css'
-
-function MyApp({ Component, pageProps }) {
- return
-}
-
-export default MyApp
diff --git a/pages/api/hello.js b/pages/api/hello.js
deleted file mode 100644
index df63de88..00000000
--- a/pages/api/hello.js
+++ /dev/null
@@ -1,5 +0,0 @@
-// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
-
-export default function handler(req, res) {
- res.status(200).json({ name: 'John Doe' })
-}
diff --git a/pages/index.js b/pages/index.js
deleted file mode 100644
index dc4b6403..00000000
--- a/pages/index.js
+++ /dev/null
@@ -1,69 +0,0 @@
-import Head from 'next/head'
-import Image from 'next/image'
-import styles from '../styles/Home.module.css'
-
-export default function Home() {
- return (
-
-
-
Create Next App
-
-
-
-
-
-
-
-
- Get started by editing{' '}
- pages/index.js
-
-
-
-
-
-
-
- )
-}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f778d521..a026931a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1,20 +1,46 @@
lockfileVersion: 5.4
specifiers:
+ '@headlessui/react': ^1.6.6
+ '@tailwindcss/forms': ^0.5.2
+ autoprefixer: ^10.4.8
+ dockerode: ^3.3.3
eslint: 8.22.0
eslint-config-next: 12.2.5
+ js-yaml: ^4.1.0
+ memory-cache: ^0.2.0
next: 12.2.5
+ node-os-utils: ^1.3.7
+ postcss: ^8.4.16
react: 18.2.0
react-dom: 18.2.0
+ react-icons: ^4.4.0
+ sharp: ^0.30.7
+ swr: ^1.3.0
+ tailwindcss: ^3.1.8
+ typescript: ^4.7.4
dependencies:
+ '@headlessui/react': 1.6.6_biqbaboplfbrettd7655fr4n2y
+ '@tailwindcss/forms': 0.5.2_tailwindcss@3.1.8
+ dockerode: 3.3.3
+ js-yaml: 4.1.0
+ memory-cache: 0.2.0
next: 12.2.5_biqbaboplfbrettd7655fr4n2y
+ node-os-utils: 1.3.7
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
+ react-icons: 4.4.0_react@18.2.0
+ sharp: 0.30.7
+ swr: 1.3.0_react@18.2.0
devDependencies:
+ autoprefixer: 10.4.8_postcss@8.4.16
eslint: 8.22.0
- eslint-config-next: 12.2.5_eslint@8.22.0
+ eslint-config-next: 12.2.5_4rv7y5c6xz3vfxwhbrcxxi73bq
+ postcss: 8.4.16
+ tailwindcss: 3.1.8
+ typescript: 4.7.4
packages:
@@ -50,6 +76,17 @@ packages:
- supports-color
dev: true
+ /@headlessui/react/1.6.6_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-MFJtmj9Xh/hhBMhLccGbBoSk+sk61BlP6sJe4uQcVMtXZhCgGqd2GyIQzzmsdPdTEWGSF434CBi8mnhR6um46Q==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ react: ^16 || ^17 || ^18
+ react-dom: ^16 || ^17 || ^18
+ dependencies:
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ dev: false
+
/@humanwhocodes/config-array/0.10.4:
resolution: {integrity: sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==}
engines: {node: '>=10.10.0'}
@@ -202,12 +239,10 @@ packages:
dependencies:
'@nodelib/fs.stat': 2.0.5
run-parallel: 1.2.0
- dev: true
/@nodelib/fs.stat/2.0.5:
resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
engines: {node: '>= 8'}
- dev: true
/@nodelib/fs.walk/1.2.8:
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
@@ -215,7 +250,6 @@ packages:
dependencies:
'@nodelib/fs.scandir': 2.1.5
fastq: 1.13.0
- dev: true
/@rushstack/eslint-patch/1.1.4:
resolution: {integrity: sha512-LwzQKA4vzIct1zNZzBmRKI9QuNpLgTQMEjsQLf3BXuGYb3QPTP4Yjf6mkdX+X1mYttZ808QpOwAzZjv28kq7DA==}
@@ -227,11 +261,20 @@ packages:
tslib: 2.4.0
dev: false
+ /@tailwindcss/forms/0.5.2_tailwindcss@3.1.8:
+ resolution: {integrity: sha512-pSrFeJB6Bg1Mrg9CdQW3+hqZXAKsBrSG9MAfFLKy1pVA4Mb4W7C0k7mEhlmS2Dfo/otxrQOET7NJiJ9RrS563w==}
+ peerDependencies:
+ tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1'
+ dependencies:
+ mini-svg-data-uri: 1.4.4
+ tailwindcss: 3.1.8
+ dev: false
+
/@types/json5/0.0.29:
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
dev: true
- /@typescript-eslint/parser/5.33.0_eslint@8.22.0:
+ /@typescript-eslint/parser/5.33.0_4rv7y5c6xz3vfxwhbrcxxi73bq:
resolution: {integrity: sha512-cgM5cJrWmrDV2KpvlcSkelTBASAs1mgqq+IUGKJvFxWrapHpaRy5EXPQz9YaKF3nZ8KY18ILTiVpUtbIac86/w==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -243,9 +286,10 @@ packages:
dependencies:
'@typescript-eslint/scope-manager': 5.33.0
'@typescript-eslint/types': 5.33.0
- '@typescript-eslint/typescript-estree': 5.33.0
+ '@typescript-eslint/typescript-estree': 5.33.0_typescript@4.7.4
debug: 4.3.4
eslint: 8.22.0
+ typescript: 4.7.4
transitivePeerDependencies:
- supports-color
dev: true
@@ -263,7 +307,7 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
- /@typescript-eslint/typescript-estree/5.33.0:
+ /@typescript-eslint/typescript-estree/5.33.0_typescript@4.7.4:
resolution: {integrity: sha512-tqq3MRLlggkJKJUrzM6wltk8NckKyyorCSGMq4eVkyL5sDYzJJcMgZATqmF8fLdsWrW7OjjIZ1m9v81vKcaqwQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -278,7 +322,8 @@ packages:
globby: 11.1.0
is-glob: 4.0.3
semver: 7.3.7
- tsutils: 3.21.0
+ tsutils: 3.21.0_typescript@4.7.4
+ typescript: 4.7.4
transitivePeerDependencies:
- supports-color
dev: true
@@ -299,6 +344,22 @@ packages:
acorn: 8.8.0
dev: true
+ /acorn-node/1.8.2:
+ resolution: {integrity: sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==}
+ dependencies:
+ acorn: 7.4.1
+ acorn-walk: 7.2.0
+ xtend: 4.0.2
+
+ /acorn-walk/7.2.0:
+ resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==}
+ engines: {node: '>=0.4.0'}
+
+ /acorn/7.4.1:
+ resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
/acorn/8.8.0:
resolution: {integrity: sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==}
engines: {node: '>=0.4.0'}
@@ -326,9 +387,18 @@ packages:
color-convert: 2.0.1
dev: true
+ /anymatch/3.1.2:
+ resolution: {integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==}
+ engines: {node: '>= 8'}
+ dependencies:
+ normalize-path: 3.0.0
+ picomatch: 2.3.1
+
+ /arg/5.0.2:
+ resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
+
/argparse/2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
- dev: true
/aria-query/4.2.2:
resolution: {integrity: sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==}
@@ -374,10 +444,32 @@ packages:
es-shim-unscopables: 1.0.0
dev: true
+ /asn1/0.2.6:
+ resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==}
+ dependencies:
+ safer-buffer: 2.1.2
+ dev: false
+
/ast-types-flow/0.0.7:
resolution: {integrity: sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==}
dev: true
+ /autoprefixer/10.4.8_postcss@8.4.16:
+ resolution: {integrity: sha512-75Jr6Q/XpTqEf6D2ltS5uMewJIx5irCU1oBYJrWjFenq/m12WRRrz6g15L1EIoYvPLXTbEry7rDOwrcYNj77xw==}
+ engines: {node: ^10 || ^12 || >=14}
+ hasBin: true
+ peerDependencies:
+ postcss: ^8.1.0
+ dependencies:
+ browserslist: 4.21.3
+ caniuse-lite: 1.0.30001375
+ fraction.js: 4.2.0
+ normalize-range: 0.1.2
+ picocolors: 1.0.0
+ postcss: 8.4.16
+ postcss-value-parser: 4.2.0
+ dev: true
+
/axe-core/4.4.3:
resolution: {integrity: sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w==}
engines: {node: '>=4'}
@@ -391,6 +483,28 @@ packages:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
dev: true
+ /base64-js/1.5.1:
+ resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
+ dev: false
+
+ /bcrypt-pbkdf/1.0.2:
+ resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==}
+ dependencies:
+ tweetnacl: 0.14.5
+ dev: false
+
+ /binary-extensions/2.2.0:
+ resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
+ engines: {node: '>=8'}
+
+ /bl/4.1.0:
+ resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
+ dependencies:
+ buffer: 5.7.1
+ inherits: 2.0.4
+ readable-stream: 3.6.0
+ dev: false
+
/brace-expansion/1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
dependencies:
@@ -403,8 +517,31 @@ packages:
engines: {node: '>=8'}
dependencies:
fill-range: 7.0.1
+
+ /browserslist/4.21.3:
+ resolution: {integrity: sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+ dependencies:
+ caniuse-lite: 1.0.30001375
+ electron-to-chromium: 1.4.219
+ node-releases: 2.0.6
+ update-browserslist-db: 1.0.5_browserslist@4.21.3
dev: true
+ /buffer/5.7.1:
+ resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
+ dependencies:
+ base64-js: 1.5.1
+ ieee754: 1.2.1
+ dev: false
+
+ /buildcheck/0.0.3:
+ resolution: {integrity: sha512-pziaA+p/wdVImfcbsZLNF32EiWyujlQLwolMqUQE8xpKNOH7KmZQaY8sXN7DGOEzPAElo9QTaeNRfGnf3iOJbA==}
+ engines: {node: '>=10.0.0'}
+ dev: false
+ optional: true
+
/call-bind/1.0.2:
resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==}
dependencies:
@@ -417,9 +554,12 @@ packages:
engines: {node: '>=6'}
dev: true
+ /camelcase-css/2.0.1:
+ resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
+ engines: {node: '>= 6'}
+
/caniuse-lite/1.0.30001375:
resolution: {integrity: sha512-kWIMkNzLYxSvnjy0hL8w1NOaWNr2rn39RTAVyIwcw8juu60bZDWiF1/loOYANzjtJmy6qPgNmn38ro5Pygagdw==}
- dev: false
/chalk/4.1.2:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
@@ -429,16 +569,47 @@ packages:
supports-color: 7.2.0
dev: true
+ /chokidar/3.5.3:
+ resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
+ engines: {node: '>= 8.10.0'}
+ dependencies:
+ anymatch: 3.1.2
+ braces: 3.0.2
+ glob-parent: 5.1.2
+ is-binary-path: 2.1.0
+ is-glob: 4.0.3
+ normalize-path: 3.0.0
+ readdirp: 3.6.0
+ optionalDependencies:
+ fsevents: 2.3.2
+
+ /chownr/1.1.4:
+ resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
+ dev: false
+
/color-convert/2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
dependencies:
color-name: 1.1.4
- dev: true
/color-name/1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
- dev: true
+
+ /color-string/1.9.1:
+ resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
+ dependencies:
+ color-name: 1.1.4
+ simple-swizzle: 0.2.2
+ dev: false
+
+ /color/4.2.3:
+ resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
+ engines: {node: '>=12.5.0'}
+ dependencies:
+ color-convert: 2.0.1
+ color-string: 1.9.1
+ dev: false
/concat-map/0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
@@ -449,6 +620,16 @@ packages:
requiresBuild: true
dev: true
+ /cpu-features/0.0.4:
+ resolution: {integrity: sha512-fKiZ/zp1mUwQbnzb9IghXtHtDoTMtNeb8oYGx6kX2SYfhnG0HNdBEBIzB9b5KlXu5DQPhfy3mInbBxFcgwAr3A==}
+ engines: {node: '>=10.0.0'}
+ requiresBuild: true
+ dependencies:
+ buildcheck: 0.0.3
+ nan: 2.16.0
+ dev: false
+ optional: true
+
/cross-spawn/7.0.3:
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
engines: {node: '>= 8'}
@@ -458,6 +639,11 @@ packages:
which: 2.0.2
dev: true
+ /cssesc/3.0.0:
+ resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
+ engines: {node: '>=4'}
+ hasBin: true
+
/damerau-levenshtein/1.0.8:
resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
dev: true
@@ -494,7 +680,18 @@ packages:
optional: true
dependencies:
ms: 2.1.2
- dev: true
+
+ /decompress-response/6.0.0:
+ resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
+ engines: {node: '>=10'}
+ dependencies:
+ mimic-response: 3.1.0
+ dev: false
+
+ /deep-extend/0.6.0:
+ resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
+ engines: {node: '>=4.0.0'}
+ dev: false
/deep-is/0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
@@ -508,6 +705,26 @@ packages:
object-keys: 1.1.1
dev: true
+ /defined/1.0.0:
+ resolution: {integrity: sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==}
+
+ /detect-libc/2.0.1:
+ resolution: {integrity: sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==}
+ engines: {node: '>=8'}
+ dev: false
+
+ /detective/5.2.1:
+ resolution: {integrity: sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==}
+ engines: {node: '>=0.8.0'}
+ hasBin: true
+ dependencies:
+ acorn-node: 1.8.2
+ defined: 1.0.0
+ minimist: 1.2.6
+
+ /didyoumean/1.2.2:
+ resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
+
/dir-glob/3.0.1:
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
engines: {node: '>=8'}
@@ -515,6 +732,31 @@ packages:
path-type: 4.0.0
dev: true
+ /dlv/1.1.3:
+ resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
+
+ /docker-modem/3.0.5:
+ resolution: {integrity: sha512-x1E6jxWdtoK3+ifAUWj4w5egPdTDGBpesSCErm+aKET5BnnEOvDtTP6GxcnMB1zZiv2iQ0qJZvJie+1wfIRg6Q==}
+ engines: {node: '>= 8.0'}
+ dependencies:
+ debug: 4.3.4
+ readable-stream: 3.6.0
+ split-ca: 1.0.1
+ ssh2: 1.11.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
+ /dockerode/3.3.3:
+ resolution: {integrity: sha512-lvKV6/NGf2/CYLt5V4c0fd6Fl9XZSCo1Z2HBT9ioKrKLMB2o+gA62Uza8RROpzGvYv57KJx2dKu+ZwSpB//OIA==}
+ engines: {node: '>= 8.0'}
+ dependencies:
+ docker-modem: 3.0.5
+ tar-fs: 2.0.1
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
/doctrine/2.1.0:
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
engines: {node: '>=0.10.0'}
@@ -529,10 +771,20 @@ packages:
esutils: 2.0.3
dev: true
+ /electron-to-chromium/1.4.219:
+ resolution: {integrity: sha512-zoQJsXOUw0ZA0YxbjkmzBumAJRtr6je5JySuL/bAoFs0DuLiLJ+5FzRF7/ZayihxR2QcewlRZVm5QZdUhwjOgA==}
+ dev: true
+
/emoji-regex/9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
dev: true
+ /end-of-stream/1.4.4:
+ resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
+ dependencies:
+ once: 1.4.0
+ dev: false
+
/es-abstract/1.20.1:
resolution: {integrity: sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==}
engines: {node: '>= 0.4'}
@@ -577,12 +829,17 @@ packages:
is-symbol: 1.0.4
dev: true
+ /escalade/3.1.1:
+ resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
+ engines: {node: '>=6'}
+ dev: true
+
/escape-string-regexp/4.0.0:
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
engines: {node: '>=10'}
dev: true
- /eslint-config-next/12.2.5_eslint@8.22.0:
+ /eslint-config-next/12.2.5_4rv7y5c6xz3vfxwhbrcxxi73bq:
resolution: {integrity: sha512-SOowilkqPzW6DxKp3a3SYlrfPi5Ajs9MIzp9gVfUDxxH9QFM5ElkR1hX5m/iICJuvCbWgQqFBiA3mCMozluniw==}
peerDependencies:
eslint: ^7.23.0 || ^8.0.0
@@ -593,7 +850,7 @@ packages:
dependencies:
'@next/eslint-plugin-next': 12.2.5
'@rushstack/eslint-patch': 1.1.4
- '@typescript-eslint/parser': 5.33.0_eslint@8.22.0
+ '@typescript-eslint/parser': 5.33.0_4rv7y5c6xz3vfxwhbrcxxi73bq
eslint: 8.22.0
eslint-import-resolver-node: 0.3.6
eslint-import-resolver-typescript: 2.7.1_2iahngt3u2tkbdlu6s4gkur3pu
@@ -601,6 +858,7 @@ packages:
eslint-plugin-jsx-a11y: 6.6.1_eslint@8.22.0
eslint-plugin-react: 7.30.1_eslint@8.22.0
eslint-plugin-react-hooks: 4.6.0_eslint@8.22.0
+ typescript: 4.7.4
transitivePeerDependencies:
- eslint-import-resolver-webpack
- supports-color
@@ -654,7 +912,7 @@ packages:
eslint-import-resolver-webpack:
optional: true
dependencies:
- '@typescript-eslint/parser': 5.33.0_eslint@8.22.0
+ '@typescript-eslint/parser': 5.33.0_4rv7y5c6xz3vfxwhbrcxxi73bq
debug: 3.2.7
eslint: 8.22.0
eslint-import-resolver-node: 0.3.6
@@ -673,7 +931,7 @@ packages:
'@typescript-eslint/parser':
optional: true
dependencies:
- '@typescript-eslint/parser': 5.33.0_eslint@8.22.0
+ '@typescript-eslint/parser': 5.33.0_4rv7y5c6xz3vfxwhbrcxxi73bq
array-includes: 3.1.5
array.prototype.flat: 1.3.0
debug: 2.6.9
@@ -857,6 +1115,11 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
+ /expand-template/2.0.3:
+ resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
+ engines: {node: '>=6'}
+ dev: false
+
/fast-deep-equal/3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
dev: true
@@ -870,7 +1133,6 @@ packages:
glob-parent: 5.1.2
merge2: 1.4.1
micromatch: 4.0.5
- dev: true
/fast-json-stable-stringify/2.1.0:
resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
@@ -884,7 +1146,6 @@ packages:
resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==}
dependencies:
reusify: 1.0.4
- dev: true
/file-entry-cache/6.0.1:
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
@@ -898,7 +1159,6 @@ packages:
engines: {node: '>=8'}
dependencies:
to-regex-range: 5.0.1
- dev: true
/find-up/5.0.0:
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
@@ -920,13 +1180,27 @@ packages:
resolution: {integrity: sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==}
dev: true
+ /fraction.js/4.2.0:
+ resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==}
+ dev: true
+
+ /fs-constants/1.0.0:
+ resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
+ dev: false
+
/fs.realpath/1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
dev: true
+ /fsevents/2.3.2:
+ resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+ requiresBuild: true
+ optional: true
+
/function-bind/1.1.1:
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
- dev: true
/function.prototype.name/1.1.5:
resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==}
@@ -962,19 +1236,21 @@ packages:
get-intrinsic: 1.1.2
dev: true
+ /github-from-package/0.0.0:
+ resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
+ dev: false
+
/glob-parent/5.1.2:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'}
dependencies:
is-glob: 4.0.3
- dev: true
/glob-parent/6.0.2:
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
engines: {node: '>=10.13.0'}
dependencies:
is-glob: 4.0.3
- dev: true
/glob/7.1.7:
resolution: {integrity: sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==}
@@ -1053,7 +1329,10 @@ packages:
engines: {node: '>= 0.4.0'}
dependencies:
function-bind: 1.1.1
- dev: true
+
+ /ieee754/1.2.1:
+ resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
+ dev: false
/ignore/5.2.0:
resolution: {integrity: sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==}
@@ -1082,7 +1361,10 @@ packages:
/inherits/2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
- dev: true
+
+ /ini/1.3.8:
+ resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
+ dev: false
/internal-slot/1.0.3:
resolution: {integrity: sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==}
@@ -1093,12 +1375,22 @@ packages:
side-channel: 1.0.4
dev: true
+ /is-arrayish/0.3.2:
+ resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
+ dev: false
+
/is-bigint/1.0.4:
resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
dependencies:
has-bigints: 1.0.2
dev: true
+ /is-binary-path/2.1.0:
+ resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+ engines: {node: '>=8'}
+ dependencies:
+ binary-extensions: 2.2.0
+
/is-boolean-object/1.1.2:
resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
engines: {node: '>= 0.4'}
@@ -1116,7 +1408,6 @@ packages:
resolution: {integrity: sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==}
dependencies:
has: 1.0.3
- dev: true
/is-date-object/1.0.5:
resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==}
@@ -1128,14 +1419,12 @@ packages:
/is-extglob/2.1.1:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
- dev: true
/is-glob/4.0.3:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
dependencies:
is-extglob: 2.1.1
- dev: true
/is-negative-zero/2.0.2:
resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==}
@@ -1152,7 +1441,6 @@ packages:
/is-number/7.0.0:
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
engines: {node: '>=0.12.0'}
- dev: true
/is-regex/1.1.4:
resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
@@ -1200,7 +1488,6 @@ packages:
hasBin: true
dependencies:
argparse: 2.0.1
- dev: true
/json-schema-traverse/0.4.1:
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
@@ -1243,6 +1530,10 @@ packages:
type-check: 0.4.0
dev: true
+ /lilconfig/2.0.6:
+ resolution: {integrity: sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==}
+ engines: {node: '>=10'}
+
/locate-path/6.0.0:
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
engines: {node: '>=10'}
@@ -1265,12 +1556,14 @@ packages:
engines: {node: '>=10'}
dependencies:
yallist: 4.0.0
- dev: true
+
+ /memory-cache/0.2.0:
+ resolution: {integrity: sha512-OcjA+jzjOYzKmKS6IQVALHLVz+rNTMPoJvCztFaZxwG14wtAW7VRZjwTQu06vKCYOxh4jVnik7ya0SXTB0W+xA==}
+ dev: false
/merge2/1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
- dev: true
/micromatch/4.0.5:
resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
@@ -1278,7 +1571,16 @@ packages:
dependencies:
braces: 3.0.2
picomatch: 2.3.1
- dev: true
+
+ /mimic-response/3.1.0:
+ resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
+ engines: {node: '>=10'}
+ dev: false
+
+ /mini-svg-data-uri/1.4.4:
+ resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==}
+ hasBin: true
+ dev: false
/minimatch/3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
@@ -1288,7 +1590,10 @@ packages:
/minimist/1.2.6:
resolution: {integrity: sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==}
- dev: true
+
+ /mkdirp-classic/0.5.3:
+ resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
+ dev: false
/ms/2.0.0:
resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
@@ -1296,16 +1601,23 @@ packages:
/ms/2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
- dev: true
/ms/2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
dev: true
+ /nan/2.16.0:
+ resolution: {integrity: sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==}
+ dev: false
+ optional: true
+
/nanoid/3.3.4:
resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
+
+ /napi-build-utils/1.0.2:
+ resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==}
dev: false
/natural-compare/1.4.0:
@@ -1357,11 +1669,43 @@ packages:
- babel-plugin-macros
dev: false
+ /node-abi/3.24.0:
+ resolution: {integrity: sha512-YPG3Co0luSu6GwOBsmIdGW6Wx0NyNDLg/hriIyDllVsNwnI6UeqaWShxC3lbH4LtEQUgoLP3XR1ndXiDAWvmRw==}
+ engines: {node: '>=10'}
+ dependencies:
+ semver: 7.3.7
+ dev: false
+
+ /node-addon-api/5.0.0:
+ resolution: {integrity: sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==}
+ dev: false
+
+ /node-os-utils/1.3.7:
+ resolution: {integrity: sha512-fvnX9tZbR7WfCG5BAy3yO/nCLyjVWD6MghEq0z5FDfN+ZXpLWNITBdbifxQkQ25ebr16G0N7eRWJisOcMEHG3Q==}
+ dev: false
+
+ /node-releases/2.0.6:
+ resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==}
+ dev: true
+
+ /normalize-path/3.0.0:
+ resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+ engines: {node: '>=0.10.0'}
+
+ /normalize-range/0.1.2:
+ resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
/object-assign/4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
dev: true
+ /object-hash/3.0.0:
+ resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
+ engines: {node: '>= 6'}
+
/object-inspect/1.12.2:
resolution: {integrity: sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==}
dev: true
@@ -1419,7 +1763,6 @@ packages:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
dependencies:
wrappy: 1.0.2
- dev: true
/optionator/0.9.1:
resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==}
@@ -1471,7 +1814,6 @@ packages:
/path-parse/1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
- dev: true
/path-type/4.0.0:
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
@@ -1480,12 +1822,69 @@ packages:
/picocolors/1.0.0:
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
- dev: false
/picomatch/2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
- dev: true
+
+ /pify/2.3.0:
+ resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
+ engines: {node: '>=0.10.0'}
+
+ /postcss-import/14.1.0_postcss@8.4.16:
+ resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ postcss: ^8.0.0
+ dependencies:
+ postcss: 8.4.16
+ postcss-value-parser: 4.2.0
+ read-cache: 1.0.0
+ resolve: 1.22.1
+
+ /postcss-js/4.0.0_postcss@8.4.16:
+ resolution: {integrity: sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==}
+ engines: {node: ^12 || ^14 || >= 16}
+ peerDependencies:
+ postcss: ^8.3.3
+ dependencies:
+ camelcase-css: 2.0.1
+ postcss: 8.4.16
+
+ /postcss-load-config/3.1.4_postcss@8.4.16:
+ resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==}
+ engines: {node: '>= 10'}
+ peerDependencies:
+ postcss: '>=8.0.9'
+ ts-node: '>=9.0.0'
+ peerDependenciesMeta:
+ postcss:
+ optional: true
+ ts-node:
+ optional: true
+ dependencies:
+ lilconfig: 2.0.6
+ postcss: 8.4.16
+ yaml: 1.10.2
+
+ /postcss-nested/5.0.6_postcss@8.4.16:
+ resolution: {integrity: sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==}
+ engines: {node: '>=12.0'}
+ peerDependencies:
+ postcss: ^8.2.14
+ dependencies:
+ postcss: 8.4.16
+ postcss-selector-parser: 6.0.10
+
+ /postcss-selector-parser/6.0.10:
+ resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==}
+ engines: {node: '>=4'}
+ dependencies:
+ cssesc: 3.0.0
+ util-deprecate: 1.0.2
+
+ /postcss-value-parser/4.2.0:
+ resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
/postcss/8.4.14:
resolution: {integrity: sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==}
@@ -1496,6 +1895,33 @@ packages:
source-map-js: 1.0.2
dev: false
+ /postcss/8.4.16:
+ resolution: {integrity: sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==}
+ engines: {node: ^10 || ^12 || >=14}
+ dependencies:
+ nanoid: 3.3.4
+ picocolors: 1.0.0
+ source-map-js: 1.0.2
+
+ /prebuild-install/7.1.1:
+ resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==}
+ engines: {node: '>=10'}
+ hasBin: true
+ dependencies:
+ detect-libc: 2.0.1
+ expand-template: 2.0.3
+ github-from-package: 0.0.0
+ minimist: 1.2.6
+ mkdirp-classic: 0.5.3
+ napi-build-utils: 1.0.2
+ node-abi: 3.24.0
+ pump: 3.0.0
+ rc: 1.2.8
+ simple-get: 4.0.1
+ tar-fs: 2.1.1
+ tunnel-agent: 0.6.0
+ dev: false
+
/prelude-ls/1.2.1:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
@@ -1509,6 +1935,13 @@ packages:
react-is: 16.13.1
dev: true
+ /pump/3.0.0:
+ resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
+ dependencies:
+ end-of-stream: 1.4.4
+ once: 1.4.0
+ dev: false
+
/punycode/2.1.1:
resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==}
engines: {node: '>=6'}
@@ -1516,7 +1949,20 @@ packages:
/queue-microtask/1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
- dev: true
+
+ /quick-lru/5.1.1:
+ resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==}
+ engines: {node: '>=10'}
+
+ /rc/1.2.8:
+ resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
+ hasBin: true
+ dependencies:
+ deep-extend: 0.6.0
+ ini: 1.3.8
+ minimist: 1.2.6
+ strip-json-comments: 2.0.1
+ dev: false
/react-dom/18.2.0_react@18.2.0:
resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==}
@@ -1528,6 +1974,14 @@ packages:
scheduler: 0.23.0
dev: false
+ /react-icons/4.4.0_react@18.2.0:
+ resolution: {integrity: sha512-fSbvHeVYo/B5/L4VhB7sBA1i2tS8MkT0Hb9t2H1AVPkwGfVHLJCqyr2Py9dKMxsyM63Eng1GkdZfbWj+Fmv8Rg==}
+ peerDependencies:
+ react: '*'
+ dependencies:
+ react: 18.2.0
+ dev: false
+
/react-is/16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
dev: true
@@ -1539,6 +1993,26 @@ packages:
loose-envify: 1.4.0
dev: false
+ /read-cache/1.0.0:
+ resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
+ dependencies:
+ pify: 2.3.0
+
+ /readable-stream/3.6.0:
+ resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==}
+ engines: {node: '>= 6'}
+ dependencies:
+ inherits: 2.0.4
+ string_decoder: 1.3.0
+ util-deprecate: 1.0.2
+ dev: false
+
+ /readdirp/3.6.0:
+ resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+ engines: {node: '>=8.10.0'}
+ dependencies:
+ picomatch: 2.3.1
+
/regenerator-runtime/0.13.9:
resolution: {integrity: sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==}
dev: true
@@ -1569,7 +2043,6 @@ packages:
is-core-module: 2.10.0
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
- dev: true
/resolve/2.0.0-next.4:
resolution: {integrity: sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==}
@@ -1583,7 +2056,6 @@ packages:
/reusify/1.0.4:
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
- dev: true
/rimraf/3.0.2:
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
@@ -1596,7 +2068,14 @@ packages:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
dependencies:
queue-microtask: 1.2.3
- dev: true
+
+ /safe-buffer/5.2.1:
+ resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+ dev: false
+
+ /safer-buffer/2.1.2:
+ resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+ dev: false
/scheduler/0.23.0:
resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==}
@@ -1615,7 +2094,21 @@ packages:
hasBin: true
dependencies:
lru-cache: 6.0.0
- dev: true
+
+ /sharp/0.30.7:
+ resolution: {integrity: sha512-G+MY2YW33jgflKPTXXptVO28HvNOo9G3j0MybYAHeEmby+QuD2U98dT6ueht9cv/XDqZspSpIhoSW+BAKJ7Hig==}
+ engines: {node: '>=12.13.0'}
+ requiresBuild: true
+ dependencies:
+ color: 4.2.3
+ detect-libc: 2.0.1
+ node-addon-api: 5.0.0
+ prebuild-install: 7.1.1
+ semver: 7.3.7
+ simple-get: 4.0.1
+ tar-fs: 2.1.1
+ tunnel-agent: 0.6.0
+ dev: false
/shebang-command/2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
@@ -1637,6 +2130,24 @@ packages:
object-inspect: 1.12.2
dev: true
+ /simple-concat/1.0.1:
+ resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
+ dev: false
+
+ /simple-get/4.0.1:
+ resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
+ dependencies:
+ decompress-response: 6.0.0
+ once: 1.4.0
+ simple-concat: 1.0.1
+ dev: false
+
+ /simple-swizzle/0.2.2:
+ resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
+ dependencies:
+ is-arrayish: 0.3.2
+ dev: false
+
/slash/3.0.0:
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
engines: {node: '>=8'}
@@ -1645,6 +2156,21 @@ packages:
/source-map-js/1.0.2:
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
engines: {node: '>=0.10.0'}
+
+ /split-ca/1.0.1:
+ resolution: {integrity: sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==}
+ dev: false
+
+ /ssh2/1.11.0:
+ resolution: {integrity: sha512-nfg0wZWGSsfUe/IBJkXVll3PEZ//YH2guww+mP88gTpuSU4FtZN7zu9JoeTGOyCNx2dTDtT9fOpWwlzyj4uOOw==}
+ engines: {node: '>=10.16.0'}
+ requiresBuild: true
+ dependencies:
+ asn1: 0.2.6
+ bcrypt-pbkdf: 1.0.2
+ optionalDependencies:
+ cpu-features: 0.0.4
+ nan: 2.16.0
dev: false
/string.prototype.matchall/4.0.7:
@@ -1676,6 +2202,12 @@ packages:
es-abstract: 1.20.1
dev: true
+ /string_decoder/1.3.0:
+ resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
+ dependencies:
+ safe-buffer: 5.2.1
+ dev: false
+
/strip-ansi/6.0.1:
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
engines: {node: '>=8'}
@@ -1688,6 +2220,11 @@ packages:
engines: {node: '>=4'}
dev: true
+ /strip-json-comments/2.0.1:
+ resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
+ engines: {node: '>=0.10.0'}
+ dev: false
+
/strip-json-comments/3.1.1:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
@@ -1719,7 +2256,73 @@ packages:
/supports-preserve-symlinks-flag/1.0.0:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
- dev: true
+
+ /swr/1.3.0_react@18.2.0:
+ resolution: {integrity: sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw==}
+ peerDependencies:
+ react: ^16.11.0 || ^17.0.0 || ^18.0.0
+ dependencies:
+ react: 18.2.0
+ dev: false
+
+ /tailwindcss/3.1.8:
+ resolution: {integrity: sha512-YSneUCZSFDYMwk+TGq8qYFdCA3yfBRdBlS7txSq0LUmzyeqRe3a8fBQzbz9M3WS/iFT4BNf/nmw9mEzrnSaC0g==}
+ engines: {node: '>=12.13.0'}
+ hasBin: true
+ dependencies:
+ arg: 5.0.2
+ chokidar: 3.5.3
+ color-name: 1.1.4
+ detective: 5.2.1
+ didyoumean: 1.2.2
+ dlv: 1.1.3
+ fast-glob: 3.2.11
+ glob-parent: 6.0.2
+ is-glob: 4.0.3
+ lilconfig: 2.0.6
+ normalize-path: 3.0.0
+ object-hash: 3.0.0
+ picocolors: 1.0.0
+ postcss: 8.4.16
+ postcss-import: 14.1.0_postcss@8.4.16
+ postcss-js: 4.0.0_postcss@8.4.16
+ postcss-load-config: 3.1.4_postcss@8.4.16
+ postcss-nested: 5.0.6_postcss@8.4.16
+ postcss-selector-parser: 6.0.10
+ postcss-value-parser: 4.2.0
+ quick-lru: 5.1.1
+ resolve: 1.22.1
+ transitivePeerDependencies:
+ - ts-node
+
+ /tar-fs/2.0.1:
+ resolution: {integrity: sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==}
+ dependencies:
+ chownr: 1.1.4
+ mkdirp-classic: 0.5.3
+ pump: 3.0.0
+ tar-stream: 2.2.0
+ dev: false
+
+ /tar-fs/2.1.1:
+ resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==}
+ dependencies:
+ chownr: 1.1.4
+ mkdirp-classic: 0.5.3
+ pump: 3.0.0
+ tar-stream: 2.2.0
+ dev: false
+
+ /tar-stream/2.2.0:
+ resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
+ engines: {node: '>=6'}
+ dependencies:
+ bl: 4.1.0
+ end-of-stream: 1.4.4
+ fs-constants: 1.0.0
+ inherits: 2.0.4
+ readable-stream: 3.6.0
+ dev: false
/text-table/0.2.0:
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
@@ -1730,7 +2333,6 @@ packages:
engines: {node: '>=8.0'}
dependencies:
is-number: 7.0.0
- dev: true
/tsconfig-paths/3.14.1:
resolution: {integrity: sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==}
@@ -1749,15 +2351,26 @@ packages:
resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==}
dev: false
- /tsutils/3.21.0:
+ /tsutils/3.21.0_typescript@4.7.4:
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
engines: {node: '>= 6'}
peerDependencies:
typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
dependencies:
tslib: 1.14.1
+ typescript: 4.7.4
dev: true
+ /tunnel-agent/0.6.0:
+ resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
+ dependencies:
+ safe-buffer: 5.2.1
+ dev: false
+
+ /tweetnacl/0.14.5:
+ resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==}
+ dev: false
+
/type-check/0.4.0:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
@@ -1770,6 +2383,12 @@ packages:
engines: {node: '>=10'}
dev: true
+ /typescript/4.7.4:
+ resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==}
+ engines: {node: '>=4.2.0'}
+ hasBin: true
+ dev: true
+
/unbox-primitive/1.0.2:
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
dependencies:
@@ -1779,6 +2398,17 @@ packages:
which-boxed-primitive: 1.0.2
dev: true
+ /update-browserslist-db/1.0.5_browserslist@4.21.3:
+ resolution: {integrity: sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+ dependencies:
+ browserslist: 4.21.3
+ escalade: 3.1.1
+ picocolors: 1.0.0
+ dev: true
+
/uri-js/4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
dependencies:
@@ -1793,6 +2423,9 @@ packages:
react: 18.2.0
dev: false
+ /util-deprecate/1.0.2:
+ resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
/v8-compile-cache/2.3.0:
resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==}
dev: true
@@ -1822,11 +2455,17 @@ packages:
/wrappy/1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
- dev: true
+
+ /xtend/4.0.2:
+ resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
+ engines: {node: '>=0.4'}
/yallist/4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
- dev: true
+
+ /yaml/1.10.2:
+ resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
+ engines: {node: '>= 6'}
/yocto-queue/0.1.0:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
diff --git a/postcss.config.js b/postcss.config.js
new file mode 100644
index 00000000..33ad091d
--- /dev/null
+++ b/postcss.config.js
@@ -0,0 +1,6 @@
+module.exports = {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+}
diff --git a/src/components/bookmarks/group.jsx b/src/components/bookmarks/group.jsx
new file mode 100644
index 00000000..bf61bcd9
--- /dev/null
+++ b/src/components/bookmarks/group.jsx
@@ -0,0 +1,15 @@
+import List from "components/bookmarks/list";
+
+export default function BookmarksGroup({ group }) {
+ return (
+
+
+ {group.name}
+
+
+
+ );
+}
diff --git a/src/components/bookmarks/item.jsx b/src/components/bookmarks/item.jsx
new file mode 100644
index 00000000..e55ab145
--- /dev/null
+++ b/src/components/bookmarks/item.jsx
@@ -0,0 +1,23 @@
+export default function Item({ bookmark }) {
+ const { hostname } = new URL(bookmark.href);
+
+ return (
+ {
+ window.open(bookmark.href, "_blank").focus();
+ }}
+ key={bookmark.name}
+ className="mb-3 cursor-pointer flex rounded-md font-medium text-theme-700 hover:text-theme-800 dark:text-theme-200 dark:hover:text-theme-300 shadow-md shadow-theme-900/10 dark:shadow-theme-900 bg-white/50 hover:bg-theme-300/10 dark:bg-white/5 dark:hover:bg-white/10"
+ >
+
+ {bookmark.abbr}
+
+
+
{bookmark.name}
+
+ {hostname}
+
+
+
+ );
+}
diff --git a/src/components/bookmarks/list.jsx b/src/components/bookmarks/list.jsx
new file mode 100644
index 00000000..2c080e02
--- /dev/null
+++ b/src/components/bookmarks/list.jsx
@@ -0,0 +1,11 @@
+import Item from "components/bookmarks/item";
+
+export default function List({ bookmarks }) {
+ return (
+
+ {bookmarks.map((bookmark) => (
+
+ ))}
+
+ );
+}
diff --git a/src/components/greeting.jsx b/src/components/greeting.jsx
new file mode 100644
index 00000000..5412a0fc
--- /dev/null
+++ b/src/components/greeting.jsx
@@ -0,0 +1,20 @@
+export default function Greeting() {
+ const name = process.env.NEXT_PUBLIC_DISPLAY_NAME;
+ const hour = new Date().getHours();
+
+ let day = "day";
+
+ if (hour < 12) {
+ day = "morning";
+ } else if (hour < 17) {
+ day = "afternoon";
+ } else {
+ day = "evening";
+ }
+
+ return (
+
+ Good {day}
+
+ );
+}
diff --git a/src/components/modal.jsx b/src/components/modal.jsx
new file mode 100644
index 00000000..f683b5e9
--- /dev/null
+++ b/src/components/modal.jsx
@@ -0,0 +1,68 @@
+import { Fragment, useRef, useState, Children } from "react";
+import { Dialog, Transition } from "@headlessui/react";
+
+function classNames(...classes) {
+ return classes.filter(Boolean).join(" ");
+}
+
+const Modal = ({ Toggle, Content }) => {
+ const [open, setOpen] = useState(false);
+ const cancelButtonRef = useRef(null);
+
+ return (
+ <>
+
+
+
+
+ >
+ );
+};
+
+const ModalToggle = ({ open, setOpen, children }) => (
+ setOpen(!open)}>{children}
+);
+
+const ModalContent = ({ open, setOpen, children }) => (
+ {children}
+);
+
+Modal.Toggle = ModalToggle;
+Modal.Content = ModalContent;
+
+export default Modal;
diff --git a/src/components/services/group.jsx b/src/components/services/group.jsx
new file mode 100644
index 00000000..b4f32aa7
--- /dev/null
+++ b/src/components/services/group.jsx
@@ -0,0 +1,15 @@
+import List from "components/services/list";
+
+export default function ServicesGroup({ services }) {
+ return (
+
+
+ {services.name}
+
+
+
+ );
+}
diff --git a/src/components/services/item.jsx b/src/components/services/item.jsx
new file mode 100644
index 00000000..797c0c91
--- /dev/null
+++ b/src/components/services/item.jsx
@@ -0,0 +1,58 @@
+import Image from "next/image";
+import { useState } from "react";
+import { Disclosure, Transition } from "@headlessui/react";
+
+import StatsList from "./stats/list";
+import Status from "./status";
+import Widget from "./widget";
+
+export default function Item({ service }) {
+ const [statsOpen, setStatsOpen] = useState(false);
+ return (
+
+
+
+
+
{
+ window.open(service.href, "_blank").focus();
+ }}
+ className="flex-shrink-0 flex items-center justify-center w-12 "
+ >
+
+
+
{
+ window.open(service.href, "_blank").focus();
+ }}
+ className="flex-1 flex items-center justify-between rounded-r-md "
+ >
+
+ {service.name}
+
{service.description}
+
+
+ {service.container && (
+
+
+
+ )}
+
+
+
+
+
+
+
+
+ {service.widget &&
}
+
+
+
+ );
+}
diff --git a/src/components/services/list.jsx b/src/components/services/list.jsx
new file mode 100644
index 00000000..ee519137
--- /dev/null
+++ b/src/components/services/list.jsx
@@ -0,0 +1,11 @@
+import Item from "components/services/item";
+
+export default function List({ services }) {
+ return (
+
+ {services.map((service) => (
+
+ ))}
+
+ );
+}
diff --git a/src/components/services/stats/list.jsx b/src/components/services/stats/list.jsx
new file mode 100644
index 00000000..cb2cdfaa
--- /dev/null
+++ b/src/components/services/stats/list.jsx
@@ -0,0 +1,70 @@
+import useSWR from "swr";
+import { calculateCPUPercent, formatBytes } from "utils/stats-helpers";
+import Stat from "./stat";
+
+export default function Stats({ service }) {
+ // fast
+ const { data: statusData, error: statusError } = useSWR(
+ `/api/docker/status/${service.container}/${service.server || ""}`,
+ {
+ refreshInterval: 1500,
+ }
+ );
+
+ // takes a full second to collect stats
+ const { data: statsData, error: statsError } = useSWR(
+ `/api/docker/stats/${service.container}/${service.server || ""}`,
+ {
+ refreshInterval: 1500,
+ }
+ );
+
+ // handle errors first
+ if (statsError || statusError) {
+ return (
+
+
+
+ );
+ }
+
+ // handle the case where we get a docker error
+ if (statusData.status !== "running") {
+ return (
+
+
+
+ );
+ }
+
+ // handle the case where the container is offline
+ if (statusData.status !== "running") {
+ return (
+
+
+
+ );
+ }
+
+ // handle the case where we don't have anything yet
+ if (!statsData || !statusData) {
+ return (
+
+
+
+
+
+
+ );
+ }
+
+ // we have stats and the container is running
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/services/stats/stat.jsx b/src/components/services/stats/stat.jsx
new file mode 100644
index 00000000..57674d4c
--- /dev/null
+++ b/src/components/services/stats/stat.jsx
@@ -0,0 +1,8 @@
+export default function Stat({ value, label }) {
+ return (
+
+ );
+}
diff --git a/src/components/services/status.jsx b/src/components/services/status.jsx
new file mode 100644
index 00000000..b2c0ea4c
--- /dev/null
+++ b/src/components/services/status.jsx
@@ -0,0 +1,29 @@
+import useSWR from "swr";
+
+export default function Status({ service }) {
+ const { data, error } = useSWR(
+ `/api/docker/status/${service.container}/${service.server || ""}`
+ );
+
+ if (error) {
+ return (
+
+ );
+ }
+
+ if (data && data.status === "running") {
+ return (
+
+ );
+ }
+
+ if (data && data.status === "not found") {
+ return (
+ <>
+
+ >
+ );
+ }
+
+ return ;
+}
diff --git a/src/components/services/widget.jsx b/src/components/services/widget.jsx
new file mode 100644
index 00000000..62864cb1
--- /dev/null
+++ b/src/components/services/widget.jsx
@@ -0,0 +1,27 @@
+import Sonarr from "./widgets/sonarr";
+import Radarr from "./widgets/radarr";
+import Ombi from "./widgets/ombi";
+import Portainer from "./widgets/portainer";
+
+const widgetMappings = {
+ sonarr: Sonarr,
+ radarr: Radarr,
+ ombi: Ombi,
+ portainer: Portainer,
+};
+
+export default function Widget({ service }) {
+ const ServiceWidget = widgetMappings[service.widget.type];
+
+ if (ServiceWidget) {
+ return ;
+ }
+
+ return (
+
+
+ Missing Widget Type: {service.widget.type}
+
+
+ );
+}
diff --git a/src/components/services/widgets/ombi.jsx b/src/components/services/widgets/ombi.jsx
new file mode 100644
index 00000000..e5527cb2
--- /dev/null
+++ b/src/components/services/widgets/ombi.jsx
@@ -0,0 +1,71 @@
+import useSWR from "swr";
+
+export default function Ombi({ service }) {
+ const config = service.widget;
+
+ function buildApiUrl(endpoint) {
+ const { url } = config;
+ return `${url}/api/v1/${endpoint}`;
+ }
+
+ const fetcher = (url) => {
+ return fetch(url, {
+ method: "GET",
+ withCredentials: true,
+ credentials: "include",
+ headers: {
+ ApiKey: `${config.key}`,
+ "Content-Type": "application/json",
+ },
+ }).then((res) => res.json());
+ };
+
+ const { data: statsData, error: statsError } = useSWR(
+ buildApiUrl(`Request/count`),
+ fetcher
+ );
+
+ if (statsError) {
+ return (
+
+ );
+ }
+
+ if (!statsData) {
+ return (
+
+ );
+ }
+
+ return (
+
+
+
{statsData.pending}
+
PENDING
+
+
+
{statsData.approved}
+
APPROVED
+
+
+
{statsData.available}
+
AVAILABLE
+
+
+ );
+}
diff --git a/src/components/services/widgets/portainer.jsx b/src/components/services/widgets/portainer.jsx
new file mode 100644
index 00000000..7af630dc
--- /dev/null
+++ b/src/components/services/widgets/portainer.jsx
@@ -0,0 +1,81 @@
+import useSWR from "swr";
+
+export default function Portainer({ service }) {
+ const config = service.widget;
+
+ function buildApiUrl(endpoint) {
+ const { url, env } = config;
+ const reqUrl = new URL(`/api/endpoints/${env}/${endpoint}`, url);
+ return `/api/proxy?url=${encodeURIComponent(reqUrl)}`;
+ }
+
+ const fetcher = (url) => {
+ return fetch(url, {
+ method: "GET",
+ withCredentials: true,
+ credentials: "include",
+ headers: {
+ "X-API-Key": `${config.key}`,
+ "Content-Type": "application/json",
+ },
+ }).then((res) => res.json());
+ };
+
+ const { data: containersData, error: containersError } = useSWR(buildApiUrl(`docker/containers/json`), fetcher);
+
+ if (containersError) {
+ return (
+
+ );
+ }
+
+ if (!containersData) {
+ return (
+
+ );
+ }
+
+ if (containersData.error) {
+ return (
+
+ );
+ }
+
+ const running = containersData.filter((c) => c.State === "running").length;
+ const stopped = containersData.filter((c) => c.State === "exited").length;
+ const total = containersData.length;
+
+ return (
+
+ );
+}
diff --git a/src/components/services/widgets/radarr.jsx b/src/components/services/widgets/radarr.jsx
new file mode 100644
index 00000000..56213841
--- /dev/null
+++ b/src/components/services/widgets/radarr.jsx
@@ -0,0 +1,63 @@
+import useSWR from "swr";
+
+export default function Radarr({ service }) {
+ const config = service.widget;
+
+ function buildApiUrl(endpoint) {
+ const { url, key } = config;
+ return `${url}/api/v3/${endpoint}?apikey=${key}`;
+ }
+
+ const { data: moviesData, error: moviesError } = useSWR(buildApiUrl("movie"));
+
+ const { data: queuedData, error: queuedError } = useSWR(
+ buildApiUrl("queue/status")
+ );
+
+ if (moviesError || queuedError) {
+ return (
+
+ );
+ }
+
+ if (!moviesData || !queuedData) {
+ return (
+
+ );
+ }
+
+ const wanted = moviesData.filter((movie) => movie.isAvailable === false);
+ const have = moviesData.filter((movie) => movie.isAvailable === true);
+
+ return (
+
+
+
{wanted.length}
+
WANTED
+
+
+
{queuedData.totalCount}
+
QUEUED
+
+
+
{moviesData.length}
+
MOVIES
+
+
+ );
+}
diff --git a/src/components/services/widgets/sonarr.jsx b/src/components/services/widgets/sonarr.jsx
new file mode 100644
index 00000000..afdda396
--- /dev/null
+++ b/src/components/services/widgets/sonarr.jsx
@@ -0,0 +1,53 @@
+import useSWR from "swr";
+
+export default function Sonarr({ service }) {
+ const config = service.widget;
+
+ function buildApiUrl(endpoint) {
+ const { url, key } = config;
+ return `${url}/api/v3/${endpoint}?apikey=${key}`;
+ }
+
+ const { data: wantedData, error: wantedError } = useSWR(
+ buildApiUrl("wanted/missing")
+ );
+
+ const { data: queuedData, error: queuedError } = useSWR(buildApiUrl("queue"));
+
+ const { data: seriesData, error: seriesError } = useSWR(
+ buildApiUrl("series")
+ );
+
+ if (wantedError || queuedError || seriesError) {
+ return (
+
+ );
+ }
+
+ if (!wantedData || !queuedData || !seriesData) {
+ return (
+
+ );
+ }
+
+ return (
+
+
+
{wantedData.totalRecords}
+
WANTED
+
+
+
{queuedData.totalRecords}
+
QUEUED
+
+
+
{seriesData.length}
+
SERIES
+
+
+ );
+}
diff --git a/src/components/theme-toggle.jsx b/src/components/theme-toggle.jsx
new file mode 100644
index 00000000..da29d9d2
--- /dev/null
+++ b/src/components/theme-toggle.jsx
@@ -0,0 +1,35 @@
+import { useContext } from "react";
+import {
+ MdDarkMode,
+ MdLightMode,
+ MdToggleOff,
+ MdToggleOn,
+} from "react-icons/md";
+
+import { ThemeContext } from "utils/theme-context";
+
+export default function ThemeToggle() {
+ const { theme, setTheme } = useContext(ThemeContext);
+
+ if (!theme) {
+ return null;
+ }
+
+ return (
+
+
+ {theme === "dark" ? (
+ setTheme(theme === "dark" ? "light" : "dark")}
+ className="text-theme-800 dark:text-theme-200 w-8 h-8 cursor-pointer"
+ />
+ ) : (
+ setTheme(theme === "dark" ? "light" : "dark")}
+ className="text-theme-800 dark:text-theme-200 w-8 h-8 cursor-pointer"
+ />
+ )}
+
+
+ );
+}
diff --git a/src/components/widget.jsx b/src/components/widget.jsx
new file mode 100644
index 00000000..415cc7d4
--- /dev/null
+++ b/src/components/widget.jsx
@@ -0,0 +1,21 @@
+import Weather from "components/widgets/weather/weather";
+import Resources from "components/widgets/resources/resources";
+
+const widgetMappings = {
+ weather: Weather,
+ resources: Resources,
+};
+
+export default function Widget({ widget }) {
+ const ServiceWidget = widgetMappings[widget.type];
+
+ if (ServiceWidget) {
+ return ;
+ }
+
+ return (
+
+ Missing {widget.type}
+
+ );
+}
diff --git a/src/components/widgets/resources/resources.jsx b/src/components/widgets/resources/resources.jsx
new file mode 100644
index 00000000..d082f515
--- /dev/null
+++ b/src/components/widgets/resources/resources.jsx
@@ -0,0 +1,114 @@
+import useSWR from "swr";
+import { FiHardDrive, FiCpu } from "react-icons/fi";
+import { FaMemory } from "react-icons/fa";
+
+export default function Resources({ options }) {
+ const { data, error } = useSWR(
+ `/api/widgets/resources?disk=${options.disk}`,
+ {
+ refreshInterval: 1500,
+ }
+ );
+
+ if (error) {
+ return failed to load
;
+ }
+
+ if (!data) {
+ return (
+ <>
+ {options.disk && (
+
+
+
+
+ - GB free
+
+
+ - GB used
+
+
+
+ )}
+
+ {options.cpu && (
+
+
+
+
+ - Usage
+
+
+ - Load
+
+
+
+ )}
+
+ {options.memory && (
+
+
+
+
+ - GB Used
+
+
+ - GB Free
+
+
+
+ )}
+ >
+ );
+ }
+
+ if (data.error) {
+ return ;
+ }
+
+ return (
+ <>
+ {options.disk && (
+
+
+
+
+ {Math.round(data.drive.freeGb)} GB free
+
+
+ {Math.round(data.drive.totalGb)} GB used
+
+
+
+ )}
+
+ {options.cpu && (
+
+
+
+
+ {Math.round(data.cpu.usage)}% Usage
+
+
+ {Math.round(data.cpu.load * 100) / 100} Load
+
+
+
+ )}
+
+ {options.memory && (
+
+
+
+
+ {Math.round((data.memory.usedMemMb / 1024) * 100) / 100} GB Used
+
+
+ {Math.round((data.memory.freeMemMb / 1024) * 100) / 100} GB Free
+
+
+
+ )}
+ >
+ );
+}
diff --git a/src/components/widgets/weather/icon.jsx b/src/components/widgets/weather/icon.jsx
new file mode 100644
index 00000000..1a250fbe
--- /dev/null
+++ b/src/components/widgets/weather/icon.jsx
@@ -0,0 +1,9 @@
+import mapIcon from "utils/condition-map";
+
+export default function Icon({ condition, timeOfDay }) {
+ const Icon = mapIcon(condition, timeOfDay);
+
+ return (
+
+ );
+}
diff --git a/src/components/widgets/weather/weather.jsx b/src/components/widgets/weather/weather.jsx
new file mode 100644
index 00000000..7e42c17d
--- /dev/null
+++ b/src/components/widgets/weather/weather.jsx
@@ -0,0 +1,41 @@
+import useSWR from "swr";
+import Icon from "./icon";
+
+export default function Weather({ options }) {
+ const { data, error } = useSWR(
+ `/api/widgets/weather?lat=${options.latitude}&lon=${options.longitude}&apiKey=${options.apiKey}&duration=${options.cache}`,
+ {
+ revalidateOnFocus: false,
+ revalidateOnReconnect: false,
+ }
+ );
+
+ if (error) {
+ return failed to load
;
+ }
+
+ if (!data) {
+ return ;
+ }
+
+ if (data.error) {
+ return ;
+ }
+
+ return (
+
+
+
+
+ {Math.round(data.current.temp_f)}°
+
+
+ {data.current.condition.text}
+
+
+
+ );
+}
diff --git a/src/pages/_app.js b/src/pages/_app.js
new file mode 100644
index 00000000..2607bc9d
--- /dev/null
+++ b/src/pages/_app.js
@@ -0,0 +1,18 @@
+import { SWRConfig } from "swr";
+import "styles/globals.css";
+import "styles/weather-icons.css";
+
+function MyApp({ Component, pageProps }) {
+ return (
+
+ fetch(resource, init).then((res) => res.json()),
+ }}
+ >
+
+
+ );
+}
+
+export default MyApp;
diff --git a/src/pages/_document.js b/src/pages/_document.js
new file mode 100644
index 00000000..d69d9044
--- /dev/null
+++ b/src/pages/_document.js
@@ -0,0 +1,24 @@
+import { Html, Head, Main, NextScript } from "next/document";
+
+export default function Document() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/pages/api/bookmarks.js b/src/pages/api/bookmarks.js
new file mode 100644
index 00000000..ee77065d
--- /dev/null
+++ b/src/pages/api/bookmarks.js
@@ -0,0 +1,27 @@
+import { promises as fs } from "fs";
+import path from "path";
+import yaml from "js-yaml";
+import checkAndCopyConfig from "utils/config";
+
+export default async function handler(req, res) {
+ checkAndCopyConfig("bookmarks.yaml");
+
+ const bookmarksYaml = path.join(process.cwd(), "config", "bookmarks.yaml");
+ const fileContents = await fs.readFile(bookmarksYaml, "utf8");
+ const bookmarks = yaml.load(fileContents);
+
+ // map easy to write YAML objects into easy to consume JS arrays
+ const bookmarksArray = bookmarks.map((group) => {
+ return {
+ name: Object.keys(group)[0],
+ bookmarks: group[Object.keys(group)[0]].map((entries) => {
+ return {
+ name: Object.keys(entries)[0],
+ ...entries[Object.keys(entries)[0]][0],
+ };
+ }),
+ };
+ });
+
+ res.send(bookmarksArray);
+}
diff --git a/src/pages/api/docker/stats/[...service].js b/src/pages/api/docker/stats/[...service].js
new file mode 100644
index 00000000..af6a1a3b
--- /dev/null
+++ b/src/pages/api/docker/stats/[...service].js
@@ -0,0 +1,49 @@
+import Docker from "dockerode";
+import getDockerArguments from "utils/docker";
+
+export default async function handler(req, res) {
+ const { service } = req.query;
+ const [containerName, containerServer] = service;
+
+ if (!containerName && !containerServer) {
+ res.status(400).send({
+ error: "docker query parameters are required",
+ });
+ return;
+ }
+
+ try {
+ const docker = new Docker(await getDockerArguments(containerServer));
+ const containers = await docker.listContainers();
+
+ // bad docker connections can result in a object?
+ // in any case, this ensures the result is the expected array
+ if (!Array.isArray(containers)) {
+ return res.status(500).send({
+ error: "query failed",
+ });
+ }
+
+ const containerNames = containers.map((container) => {
+ return container.Names[0].replace(/^\//, "");
+ });
+ const containerExists = containerNames.includes(containerName);
+
+ if (!containerExists) {
+ return res.status(404).send({
+ error: "not found",
+ });
+ }
+
+ const container = docker.getContainer(containerName);
+ const stats = await container.stats({ stream: false });
+
+ return res.status(200).json({
+ stats: stats,
+ });
+ } catch {
+ return res.status(500).send({
+ error: "unknown error",
+ });
+ }
+}
diff --git a/src/pages/api/docker/status/[...service].js b/src/pages/api/docker/status/[...service].js
new file mode 100644
index 00000000..a0c8cd5a
--- /dev/null
+++ b/src/pages/api/docker/status/[...service].js
@@ -0,0 +1,48 @@
+import Docker from "dockerode";
+import getDockerArguments from "utils/docker";
+
+export default async function handler(req, res) {
+ const { service } = req.query;
+ const [containerName, containerServer] = service;
+
+ if (!containerName && !containerServer) {
+ return res.status(400).send({
+ error: "docker query parameters are required",
+ });
+ }
+
+ try {
+ const docker = new Docker(await getDockerArguments(containerServer));
+ const containers = await docker.listContainers();
+
+ // bad docker connections can result in a object?
+ // in any case, this ensures the result is the expected array
+ if (!Array.isArray(containers)) {
+ return res.status(500).send({
+ error: "query failed",
+ });
+ }
+
+ const containerNames = containers.map((container) => {
+ return container.Names[0].replace(/^\//, "");
+ });
+ const containerExists = containerNames.includes(containerName);
+
+ if (!containerExists) {
+ return res.status(404).send({
+ error: "not found",
+ });
+ }
+
+ const container = docker.getContainer(containerName);
+ const info = await container.inspect();
+
+ return res.status(200).json({
+ status: info.State.Status,
+ });
+ } catch {
+ return res.status(500).send({
+ error: "unknown error",
+ });
+ }
+}
diff --git a/src/pages/api/proxy.js b/src/pages/api/proxy.js
new file mode 100644
index 00000000..26495a4e
--- /dev/null
+++ b/src/pages/api/proxy.js
@@ -0,0 +1,29 @@
+function pick(object, keys) {
+ return;
+}
+
+export default async function handler(req, res) {
+ const headers = ["X-API-Key", "Content-Type", "Authorization"].reduce((obj, key) => {
+ if (req.headers && req.headers.hasOwnProperty(key.toLowerCase())) {
+ obj[key] = req.headers[key.toLowerCase()];
+ }
+ return obj;
+ }, {});
+
+ try {
+ const result = await fetch(req.query.url, {
+ strictSSL: false,
+ rejectUnhauthorized: false,
+ method: req.method,
+ headers: headers,
+ body: req.method == "GET" || req.method == "HEAD" ? null : req.body,
+ }).then((res) => res);
+
+ const forward = await result.text();
+ return res.status(result.status).send(forward);
+ } catch {
+ return res.status(500).send({
+ error: "query failed",
+ });
+ }
+}
diff --git a/src/pages/api/services/index.js b/src/pages/api/services/index.js
new file mode 100644
index 00000000..53d50869
--- /dev/null
+++ b/src/pages/api/services/index.js
@@ -0,0 +1,27 @@
+import { promises as fs } from "fs";
+import path from "path";
+import yaml from "js-yaml";
+import checkAndCopyConfig from "utils/config";
+
+export default async function handler(req, res) {
+ checkAndCopyConfig("services.yaml");
+
+ const servicesYaml = path.join(process.cwd(), "config", "services.yaml");
+ const fileContents = await fs.readFile(servicesYaml, "utf8");
+ const services = yaml.load(fileContents);
+
+ // map easy to write YAML objects into easy to consume JS arrays
+ const servicesArray = services.map((group) => {
+ return {
+ name: Object.keys(group)[0],
+ services: group[Object.keys(group)[0]].map((entries) => {
+ return {
+ name: Object.keys(entries)[0],
+ ...entries[Object.keys(entries)[0]],
+ };
+ }),
+ };
+ });
+
+ res.send(servicesArray);
+}
diff --git a/src/pages/api/widgets/index.js b/src/pages/api/widgets/index.js
new file mode 100644
index 00000000..98496ad6
--- /dev/null
+++ b/src/pages/api/widgets/index.js
@@ -0,0 +1,22 @@
+import { promises as fs } from "fs";
+import path from "path";
+import yaml from "js-yaml";
+import checkAndCopyConfig from "utils/config";
+
+export default async function handler(req, res) {
+ checkAndCopyConfig("widgets.yaml");
+
+ const widgetsYaml = path.join(process.cwd(), "config", "widgets.yaml");
+ const fileContents = await fs.readFile(widgetsYaml, "utf8");
+ const widgets = yaml.load(fileContents);
+
+ // map easy to write YAML objects into easy to consume JS arrays
+ const widgetsArray = widgets.map((group) => {
+ return {
+ type: Object.keys(group)[0],
+ options: { ...group[Object.keys(group)[0]] },
+ };
+ });
+
+ res.send(widgetsArray);
+}
diff --git a/src/pages/api/widgets/resources.js b/src/pages/api/widgets/resources.js
new file mode 100644
index 00000000..528f50fd
--- /dev/null
+++ b/src/pages/api/widgets/resources.js
@@ -0,0 +1,14 @@
+import { cpu, drive, mem, netstat } from "node-os-utils";
+
+export default async function handler(req, res) {
+ const { disk } = req.query;
+
+ res.send({
+ cpu: {
+ usage: await cpu.usage(),
+ load: cpu.loadavgTime(5),
+ },
+ drive: await drive.info(disk || "/"),
+ memory: await mem.info(),
+ });
+}
diff --git a/src/pages/api/widgets/weather.js b/src/pages/api/widgets/weather.js
new file mode 100644
index 00000000..5d1af4b0
--- /dev/null
+++ b/src/pages/api/widgets/weather.js
@@ -0,0 +1,9 @@
+import cachedFetch from "utils/cached-fetch";
+
+export default async function handler(req, res) {
+ const { lat, lon, apiKey, duration } = req.query;
+
+ const api_url = `http://api.weatherapi.com/v1/current.json?q=${lat},${lon}&key=${apiKey}`;
+
+ res.send(await cachedFetch(api_url, duration));
+}
diff --git a/src/pages/index.js b/src/pages/index.js
new file mode 100644
index 00000000..29958531
--- /dev/null
+++ b/src/pages/index.js
@@ -0,0 +1,58 @@
+import useSWR from "swr";
+import Head from "next/head";
+import dynamic from "next/dynamic";
+
+import { ThemeProvider } from "utils/theme-context";
+
+import ServicesGroup from "components/services/group";
+import BookmarksGroup from "components/bookmarks/group";
+import Widget from "components/widget";
+
+const ThemeToggle = dynamic(() => import("components/theme-toggle"), {
+ ssr: false,
+});
+
+export default function Home() {
+ const { data: services, error: servicesError } = useSWR("/api/services");
+ const { data: bookmarks, error: bookmarksError } = useSWR("/api/bookmarks");
+ const { data: widgets, error: widgetsError } = useSWR("/api/widgets");
+
+ return (
+
+
+ Welcome
+
+
+
+ {widgets && (
+ <>
+ {widgets.map((widget) => (
+
+ ))}
+ >
+ )}
+
+
+ {services && (
+
+ {services.map((group) => (
+
+ ))}
+
+ )}
+
+ {bookmarks && (
+
+ {bookmarks.map((group) => (
+
+ ))}
+
+ )}
+
+
+
+
+
+
+ );
+}
diff --git a/src/skeleton/bookmarks.yaml b/src/skeleton/bookmarks.yaml
new file mode 100644
index 00000000..d553b2e3
--- /dev/null
+++ b/src/skeleton/bookmarks.yaml
@@ -0,0 +1,34 @@
+- Developer: # Bookmark group title
+ - Github: # Bookmark title
+ - abbr: GH # Two letter abbrivation for the bookmark
+ href: https://github.com/ # URL to link out to
+
+ - StackOverflow:
+ - abbr: SO
+ href: https://stackoverflow.com/
+
+ - DEV:
+ - abbr: DT
+ href: https://dev.to/
+
+- Social:
+ - LinkedIn:
+ - abbr: LI
+ href: https://linkedin.com/
+
+ - Twitter:
+ - abbr: TW
+ href: https://twitter.com/
+
+- Entertainment:
+ - YouTube:
+ - abbr: YT
+ href: https://youtube.com/
+
+ - Netflix:
+ - abbr: NF
+ href: https://netflix.com/
+
+ - Reddit:
+ - abbr: RE
+ href: https://reddit.com/
diff --git a/src/skeleton/docker.yaml b/src/skeleton/docker.yaml
new file mode 100644
index 00000000..6e433193
--- /dev/null
+++ b/src/skeleton/docker.yaml
@@ -0,0 +1,10 @@
+# Docker host configuration, used for docker integrations
+# Can be either a host and port (e.g. 127.0.0.1:2375),
+# or a unix socket path (e.g. /var/run/docker.sock)
+
+my-docker:
+ host: 127.0.0.1
+ port: 2375
+
+other-docker:
+ socket: /var/run/docker.sock
diff --git a/src/skeleton/services.yaml b/src/skeleton/services.yaml
new file mode 100644
index 00000000..e51e117b
--- /dev/null
+++ b/src/skeleton/services.yaml
@@ -0,0 +1,53 @@
+- Media: # Services Group Title
+ - Emby: # Service Title
+ icon: emby.png # Icon, see: https://github.com/walkxcode/dashboard-icons or public/icons
+ href: http://emby.home/ # Link to the service
+ description: Media server # Description of the service
+ server: my-docker # Docker server the service is running on
+ container: emby # Docker container the service is running on
+
+ - Radarr:
+ icon: radarr.png
+ href: http://radarr.home/
+ description: Movie management
+ server: my-docker
+ container: radarr
+ widget: # Service widget configuration
+ type: radarr # Widget type, currently supports types radarr, sonarr, ombi or portainer
+ url: http://radarr.home # Base URL of the service, where the API is located
+ key: apikeyapikeyapikeyapikey # API key of the service
+
+- Documents & Files:
+ - ruTorrent:
+ icon: rutorrent.png
+ href: http://rutorrent.home/
+ description: Torrent downloader
+ server: my-docker
+ container: rutorrent
+
+ - File Browser:
+ icon: filebrowser.png
+ href: "#"
+ description: Media File Management
+ server: my-docker
+ container: filebrowser
+
+- Utilities:
+ - Media Portainer:
+ icon: portainer.png
+ href: http://portainer.home:9443/
+ description: Container management
+ server: my-docker
+ container: portainer
+ widget:
+ type: portainer
+ url: https://portainer.home:9443
+ env: 2
+ key: accesskeyaccesskeyaccesskeyaccesskey
+
+ - Traefik:
+ icon: traefik.png
+ href: http://traefik.home/
+ description: Reverse Proxy
+ server: my-docker
+ container: traefik
diff --git a/src/skeleton/widgets.yaml b/src/skeleton/widgets.yaml
new file mode 100644
index 00000000..e366a799
--- /dev/null
+++ b/src/skeleton/widgets.yaml
@@ -0,0 +1,11 @@
+- weather: # type of the widget
+ latitude: 51.5072 # widget configuration
+ longitude: 0.1275
+ units: metric
+ apiKey: weather_api_key # get from https://www.weatherapi.com/
+ cache: 5 # cache time in minutes
+
+- resources:
+ cpu: true
+ memory: true
+ disk: / # disk (path) to show usage for
diff --git a/src/styles/font/weathericons-regular-webfont.eot b/src/styles/font/weathericons-regular-webfont.eot
new file mode 100755
index 00000000..330b7ec7
Binary files /dev/null and b/src/styles/font/weathericons-regular-webfont.eot differ
diff --git a/src/styles/font/weathericons-regular-webfont.svg b/src/styles/font/weathericons-regular-webfont.svg
new file mode 100755
index 00000000..397d7305
--- /dev/null
+++ b/src/styles/font/weathericons-regular-webfont.svg
@@ -0,0 +1,257 @@
+
+
+
\ No newline at end of file
diff --git a/src/styles/font/weathericons-regular-webfont.ttf b/src/styles/font/weathericons-regular-webfont.ttf
new file mode 100755
index 00000000..948f0a5d
Binary files /dev/null and b/src/styles/font/weathericons-regular-webfont.ttf differ
diff --git a/src/styles/font/weathericons-regular-webfont.woff b/src/styles/font/weathericons-regular-webfont.woff
new file mode 100755
index 00000000..e0b2f948
Binary files /dev/null and b/src/styles/font/weathericons-regular-webfont.woff differ
diff --git a/src/styles/font/weathericons-regular-webfont.woff2 b/src/styles/font/weathericons-regular-webfont.woff2
new file mode 100755
index 00000000..bb0c19dd
Binary files /dev/null and b/src/styles/font/weathericons-regular-webfont.woff2 differ
diff --git a/src/styles/globals.css b/src/styles/globals.css
new file mode 100644
index 00000000..e11bf477
--- /dev/null
+++ b/src/styles/globals.css
@@ -0,0 +1,14 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+#__next {
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ padding: 0;
+}
+
+body {
+ font-family: "Poppins", sans-serif;
+}
diff --git a/src/styles/weather-icons.css b/src/styles/weather-icons.css
new file mode 100644
index 00000000..b69f78d1
--- /dev/null
+++ b/src/styles/weather-icons.css
@@ -0,0 +1,1844 @@
+/*!
+ * Weather Icons 2.0.10
+ * Updated November 1, 2020
+ * Weather themed icons for Bootstrap
+ * Author - Erik Flowers - erik@helloerik.com
+ * Email: erik@helloerik.com
+ * Twitter: http://twitter.com/Erik_UX
+ * ------------------------------------------------------------------------------
+ * Maintained at http://erikflowers.github.io/weather-icons
+ *
+ * License
+ * ------------------------------------------------------------------------------
+ * - Font licensed under SIL OFL 1.1 -
+ * http://scripts.sil.org/OFL
+ * - CSS, SCSS and LESS are licensed under MIT License -
+ * http://opensource.org/licenses/mit-license.html
+ * - Documentation licensed under CC BY 3.0 -
+ * http://creativecommons.org/licenses/by/3.0/
+ * - Inspired by and works great as a companion with Font Awesome
+ * "Font Awesome by Dave Gandy - http://fontawesome.io"
+ */
+@font-face {
+ font-family: "weathericons";
+ src: url("./font/weathericons-regular-webfont.eot");
+ src: url("./font/weathericons-regular-webfont.eot?#iefix")
+ format("embedded-opentype"),
+ url("./font/weathericons-regular-webfont.woff2") format("woff2"),
+ url("./font/weathericons-regular-webfont.woff") format("woff"),
+ url("./font/weathericons-regular-webfont.ttf") format("truetype"),
+ url("./font/weathericons-regular-webfont.svg#weather_iconsregular")
+ format("svg");
+ font-weight: normal;
+ font-style: normal;
+}
+.wi {
+ display: inline-block;
+ font-family: "weathericons";
+ font-style: normal;
+ font-weight: normal;
+ line-height: 1;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+.wi-fw {
+ text-align: center;
+ width: 1.4em;
+}
+.wi-rotate-90 {
+ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1);
+ -webkit-transform: rotate(90deg);
+ -ms-transform: rotate(90deg);
+ transform: rotate(90deg);
+}
+.wi-rotate-180 {
+ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2);
+ -webkit-transform: rotate(180deg);
+ -ms-transform: rotate(180deg);
+ transform: rotate(180deg);
+}
+.wi-rotate-270 {
+ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
+ -webkit-transform: rotate(270deg);
+ -ms-transform: rotate(270deg);
+ transform: rotate(270deg);
+}
+.wi-flip-horizontal {
+ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);
+ -webkit-transform: scale(-1, 1);
+ -ms-transform: scale(-1, 1);
+ transform: scale(-1, 1);
+}
+.wi-flip-vertical {
+ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);
+ -webkit-transform: scale(1, -1);
+ -ms-transform: scale(1, -1);
+ transform: scale(1, -1);
+}
+.wi-day-sunny:before {
+ content: "\f00d";
+}
+.wi-day-cloudy:before {
+ content: "\f002";
+}
+.wi-day-cloudy-gusts:before {
+ content: "\f000";
+}
+.wi-day-cloudy-windy:before {
+ content: "\f001";
+}
+.wi-day-fog:before {
+ content: "\f003";
+}
+.wi-day-hail:before {
+ content: "\f004";
+}
+.wi-day-haze:before {
+ content: "\f0b6";
+}
+.wi-day-lightning:before {
+ content: "\f005";
+}
+.wi-day-rain:before {
+ content: "\f008";
+}
+.wi-day-rain-mix:before {
+ content: "\f006";
+}
+.wi-day-rain-wind:before {
+ content: "\f007";
+}
+.wi-day-showers:before {
+ content: "\f009";
+}
+.wi-day-sleet:before {
+ content: "\f0b2";
+}
+.wi-day-sleet-storm:before {
+ content: "\f068";
+}
+.wi-day-snow:before {
+ content: "\f00a";
+}
+.wi-day-snow-thunderstorm:before {
+ content: "\f06b";
+}
+.wi-day-snow-wind:before {
+ content: "\f065";
+}
+.wi-day-sprinkle:before {
+ content: "\f00b";
+}
+.wi-day-storm-showers:before {
+ content: "\f00e";
+}
+.wi-day-sunny-overcast:before {
+ content: "\f00c";
+}
+.wi-day-thunderstorm:before {
+ content: "\f010";
+}
+.wi-day-windy:before {
+ content: "\f085";
+}
+.wi-solar-eclipse:before {
+ content: "\f06e";
+}
+.wi-hot:before {
+ content: "\f072";
+}
+.wi-day-cloudy-high:before {
+ content: "\f07d";
+}
+.wi-day-light-wind:before {
+ content: "\f0c4";
+}
+.wi-night-clear:before {
+ content: "\f02e";
+}
+.wi-night-alt-cloudy:before {
+ content: "\f086";
+}
+.wi-night-alt-cloudy-gusts:before {
+ content: "\f022";
+}
+.wi-night-alt-cloudy-windy:before {
+ content: "\f023";
+}
+.wi-night-alt-hail:before {
+ content: "\f024";
+}
+.wi-night-alt-lightning:before {
+ content: "\f025";
+}
+.wi-night-alt-rain:before {
+ content: "\f028";
+}
+.wi-night-alt-rain-mix:before {
+ content: "\f026";
+}
+.wi-night-alt-rain-wind:before {
+ content: "\f027";
+}
+.wi-night-alt-showers:before {
+ content: "\f029";
+}
+.wi-night-alt-sleet:before {
+ content: "\f0b4";
+}
+.wi-night-alt-sleet-storm:before {
+ content: "\f06a";
+}
+.wi-night-alt-snow:before {
+ content: "\f02a";
+}
+.wi-night-alt-snow-thunderstorm:before {
+ content: "\f06d";
+}
+.wi-night-alt-snow-wind:before {
+ content: "\f067";
+}
+.wi-night-alt-sprinkle:before {
+ content: "\f02b";
+}
+.wi-night-alt-storm-showers:before {
+ content: "\f02c";
+}
+.wi-night-alt-thunderstorm:before {
+ content: "\f02d";
+}
+.wi-night-cloudy:before {
+ content: "\f031";
+}
+.wi-night-cloudy-gusts:before {
+ content: "\f02f";
+}
+.wi-night-cloudy-windy:before {
+ content: "\f030";
+}
+.wi-night-fog:before {
+ content: "\f04a";
+}
+.wi-night-hail:before {
+ content: "\f032";
+}
+.wi-night-lightning:before {
+ content: "\f033";
+}
+.wi-night-partly-cloudy:before {
+ content: "\f083";
+}
+.wi-night-rain:before {
+ content: "\f036";
+}
+.wi-night-rain-mix:before {
+ content: "\f034";
+}
+.wi-night-rain-wind:before {
+ content: "\f035";
+}
+.wi-night-showers:before {
+ content: "\f037";
+}
+.wi-night-sleet:before {
+ content: "\f0b3";
+}
+.wi-night-sleet-storm:before {
+ content: "\f069";
+}
+.wi-night-snow:before {
+ content: "\f038";
+}
+.wi-night-snow-thunderstorm:before {
+ content: "\f06c";
+}
+.wi-night-snow-wind:before {
+ content: "\f066";
+}
+.wi-night-sprinkle:before {
+ content: "\f039";
+}
+.wi-night-storm-showers:before {
+ content: "\f03a";
+}
+.wi-night-thunderstorm:before {
+ content: "\f03b";
+}
+.wi-lunar-eclipse:before {
+ content: "\f070";
+}
+.wi-stars:before {
+ content: "\f077";
+}
+.wi-storm-showers:before {
+ content: "\f01d";
+}
+.wi-thunderstorm:before {
+ content: "\f01e";
+}
+.wi-night-alt-cloudy-high:before {
+ content: "\f07e";
+}
+.wi-night-cloudy-high:before {
+ content: "\f080";
+}
+.wi-night-alt-partly-cloudy:before {
+ content: "\f081";
+}
+.wi-cloud:before {
+ content: "\f041";
+}
+.wi-cloudy:before {
+ content: "\f013";
+}
+.wi-cloudy-gusts:before {
+ content: "\f011";
+}
+.wi-cloudy-windy:before {
+ content: "\f012";
+}
+.wi-fog:before {
+ content: "\f014";
+}
+.wi-hail:before {
+ content: "\f015";
+}
+.wi-rain:before {
+ content: "\f019";
+}
+.wi-rain-mix:before {
+ content: "\f017";
+}
+.wi-rain-wind:before {
+ content: "\f018";
+}
+.wi-showers:before {
+ content: "\f01a";
+}
+.wi-sleet:before {
+ content: "\f0b5";
+}
+.wi-snow:before {
+ content: "\f01b";
+}
+.wi-sprinkle:before {
+ content: "\f01c";
+}
+.wi-storm-showers:before {
+ content: "\f01d";
+}
+.wi-thunderstorm:before {
+ content: "\f01e";
+}
+.wi-snow-wind:before {
+ content: "\f064";
+}
+.wi-snow:before {
+ content: "\f01b";
+}
+.wi-smog:before {
+ content: "\f074";
+}
+.wi-smoke:before {
+ content: "\f062";
+}
+.wi-lightning:before {
+ content: "\f016";
+}
+.wi-raindrops:before {
+ content: "\f04e";
+}
+.wi-raindrop:before {
+ content: "\f078";
+}
+.wi-dust:before {
+ content: "\f063";
+}
+.wi-snowflake-cold:before {
+ content: "\f076";
+}
+.wi-windy:before {
+ content: "\f021";
+}
+.wi-strong-wind:before {
+ content: "\f050";
+}
+.wi-sandstorm:before {
+ content: "\f082";
+}
+.wi-earthquake:before {
+ content: "\f0c6";
+}
+.wi-fire:before {
+ content: "\f0c7";
+}
+.wi-flood:before {
+ content: "\f07c";
+}
+.wi-meteor:before {
+ content: "\f071";
+}
+.wi-tsunami:before {
+ content: "\f0c5";
+}
+.wi-volcano:before {
+ content: "\f0c8";
+}
+.wi-hurricane:before {
+ content: "\f073";
+}
+.wi-tornado:before {
+ content: "\f056";
+}
+.wi-small-craft-advisory:before {
+ content: "\f0cc";
+}
+.wi-gale-warning:before {
+ content: "\f0cd";
+}
+.wi-storm-warning:before {
+ content: "\f0ce";
+}
+.wi-hurricane-warning:before {
+ content: "\f0cf";
+}
+.wi-wind-direction:before {
+ content: "\f0b1";
+}
+.wi-alien:before {
+ content: "\f075";
+}
+.wi-celsius:before {
+ content: "\f03c";
+}
+.wi-fahrenheit:before {
+ content: "\f045";
+}
+.wi-degrees:before {
+ content: "\f042";
+}
+.wi-thermometer:before {
+ content: "\f055";
+}
+.wi-thermometer-exterior:before {
+ content: "\f053";
+}
+.wi-thermometer-internal:before {
+ content: "\f054";
+}
+.wi-cloud-down:before {
+ content: "\f03d";
+}
+.wi-cloud-up:before {
+ content: "\f040";
+}
+.wi-cloud-refresh:before {
+ content: "\f03e";
+}
+.wi-horizon:before {
+ content: "\f047";
+}
+.wi-horizon-alt:before {
+ content: "\f046";
+}
+.wi-sunrise:before {
+ content: "\f051";
+}
+.wi-sunset:before {
+ content: "\f052";
+}
+.wi-moonrise:before {
+ content: "\f0c9";
+}
+.wi-moonset:before {
+ content: "\f0ca";
+}
+.wi-refresh:before {
+ content: "\f04c";
+}
+.wi-refresh-alt:before {
+ content: "\f04b";
+}
+.wi-umbrella:before {
+ content: "\f084";
+}
+.wi-barometer:before {
+ content: "\f079";
+}
+.wi-humidity:before {
+ content: "\f07a";
+}
+.wi-na:before {
+ content: "\f07b";
+}
+.wi-train:before {
+ content: "\f0cb";
+}
+.wi-moon-new:before {
+ content: "\f095";
+}
+.wi-moon-waxing-crescent-1:before {
+ content: "\f096";
+}
+.wi-moon-waxing-crescent-2:before {
+ content: "\f097";
+}
+.wi-moon-waxing-crescent-3:before {
+ content: "\f098";
+}
+.wi-moon-waxing-crescent-4:before {
+ content: "\f099";
+}
+.wi-moon-waxing-crescent-5:before {
+ content: "\f09a";
+}
+.wi-moon-waxing-crescent-6:before {
+ content: "\f09b";
+}
+.wi-moon-first-quarter:before {
+ content: "\f09c";
+}
+.wi-moon-waxing-gibbous-1:before {
+ content: "\f09d";
+}
+.wi-moon-waxing-gibbous-2:before {
+ content: "\f09e";
+}
+.wi-moon-waxing-gibbous-3:before {
+ content: "\f09f";
+}
+.wi-moon-waxing-gibbous-4:before {
+ content: "\f0a0";
+}
+.wi-moon-waxing-gibbous-5:before {
+ content: "\f0a1";
+}
+.wi-moon-waxing-gibbous-6:before {
+ content: "\f0a2";
+}
+.wi-moon-full:before {
+ content: "\f0a3";
+}
+.wi-moon-waning-gibbous-1:before {
+ content: "\f0a4";
+}
+.wi-moon-waning-gibbous-2:before {
+ content: "\f0a5";
+}
+.wi-moon-waning-gibbous-3:before {
+ content: "\f0a6";
+}
+.wi-moon-waning-gibbous-4:before {
+ content: "\f0a7";
+}
+.wi-moon-waning-gibbous-5:before {
+ content: "\f0a8";
+}
+.wi-moon-waning-gibbous-6:before {
+ content: "\f0a9";
+}
+.wi-moon-third-quarter:before {
+ content: "\f0aa";
+}
+.wi-moon-waning-crescent-1:before {
+ content: "\f0ab";
+}
+.wi-moon-waning-crescent-2:before {
+ content: "\f0ac";
+}
+.wi-moon-waning-crescent-3:before {
+ content: "\f0ad";
+}
+.wi-moon-waning-crescent-4:before {
+ content: "\f0ae";
+}
+.wi-moon-waning-crescent-5:before {
+ content: "\f0af";
+}
+.wi-moon-waning-crescent-6:before {
+ content: "\f0b0";
+}
+.wi-moon-alt-new:before {
+ content: "\f0eb";
+}
+.wi-moon-alt-waxing-crescent-1:before {
+ content: "\f0d0";
+}
+.wi-moon-alt-waxing-crescent-2:before {
+ content: "\f0d1";
+}
+.wi-moon-alt-waxing-crescent-3:before {
+ content: "\f0d2";
+}
+.wi-moon-alt-waxing-crescent-4:before {
+ content: "\f0d3";
+}
+.wi-moon-alt-waxing-crescent-5:before {
+ content: "\f0d4";
+}
+.wi-moon-alt-waxing-crescent-6:before {
+ content: "\f0d5";
+}
+.wi-moon-alt-first-quarter:before {
+ content: "\f0d6";
+}
+.wi-moon-alt-waxing-gibbous-1:before {
+ content: "\f0d7";
+}
+.wi-moon-alt-waxing-gibbous-2:before {
+ content: "\f0d8";
+}
+.wi-moon-alt-waxing-gibbous-3:before {
+ content: "\f0d9";
+}
+.wi-moon-alt-waxing-gibbous-4:before {
+ content: "\f0da";
+}
+.wi-moon-alt-waxing-gibbous-5:before {
+ content: "\f0db";
+}
+.wi-moon-alt-waxing-gibbous-6:before {
+ content: "\f0dc";
+}
+.wi-moon-alt-full:before {
+ content: "\f0dd";
+}
+.wi-moon-alt-waning-gibbous-1:before {
+ content: "\f0de";
+}
+.wi-moon-alt-waning-gibbous-2:before {
+ content: "\f0df";
+}
+.wi-moon-alt-waning-gibbous-3:before {
+ content: "\f0e0";
+}
+.wi-moon-alt-waning-gibbous-4:before {
+ content: "\f0e1";
+}
+.wi-moon-alt-waning-gibbous-5:before {
+ content: "\f0e2";
+}
+.wi-moon-alt-waning-gibbous-6:before {
+ content: "\f0e3";
+}
+.wi-moon-alt-third-quarter:before {
+ content: "\f0e4";
+}
+.wi-moon-alt-waning-crescent-1:before {
+ content: "\f0e5";
+}
+.wi-moon-alt-waning-crescent-2:before {
+ content: "\f0e6";
+}
+.wi-moon-alt-waning-crescent-3:before {
+ content: "\f0e7";
+}
+.wi-moon-alt-waning-crescent-4:before {
+ content: "\f0e8";
+}
+.wi-moon-alt-waning-crescent-5:before {
+ content: "\f0e9";
+}
+.wi-moon-alt-waning-crescent-6:before {
+ content: "\f0ea";
+}
+.wi-moon-0:before {
+ content: "\f095";
+}
+.wi-moon-1:before {
+ content: "\f096";
+}
+.wi-moon-2:before {
+ content: "\f097";
+}
+.wi-moon-3:before {
+ content: "\f098";
+}
+.wi-moon-4:before {
+ content: "\f099";
+}
+.wi-moon-5:before {
+ content: "\f09a";
+}
+.wi-moon-6:before {
+ content: "\f09b";
+}
+.wi-moon-7:before {
+ content: "\f09c";
+}
+.wi-moon-8:before {
+ content: "\f09d";
+}
+.wi-moon-9:before {
+ content: "\f09e";
+}
+.wi-moon-10:before {
+ content: "\f09f";
+}
+.wi-moon-11:before {
+ content: "\f0a0";
+}
+.wi-moon-12:before {
+ content: "\f0a1";
+}
+.wi-moon-13:before {
+ content: "\f0a2";
+}
+.wi-moon-14:before {
+ content: "\f0a3";
+}
+.wi-moon-15:before {
+ content: "\f0a4";
+}
+.wi-moon-16:before {
+ content: "\f0a5";
+}
+.wi-moon-17:before {
+ content: "\f0a6";
+}
+.wi-moon-18:before {
+ content: "\f0a7";
+}
+.wi-moon-19:before {
+ content: "\f0a8";
+}
+.wi-moon-20:before {
+ content: "\f0a9";
+}
+.wi-moon-21:before {
+ content: "\f0aa";
+}
+.wi-moon-22:before {
+ content: "\f0ab";
+}
+.wi-moon-23:before {
+ content: "\f0ac";
+}
+.wi-moon-24:before {
+ content: "\f0ad";
+}
+.wi-moon-25:before {
+ content: "\f0ae";
+}
+.wi-moon-26:before {
+ content: "\f0af";
+}
+.wi-moon-27:before {
+ content: "\f0b0";
+}
+.wi-time-1:before {
+ content: "\f08a";
+}
+.wi-time-2:before {
+ content: "\f08b";
+}
+.wi-time-3:before {
+ content: "\f08c";
+}
+.wi-time-4:before {
+ content: "\f08d";
+}
+.wi-time-5:before {
+ content: "\f08e";
+}
+.wi-time-6:before {
+ content: "\f08f";
+}
+.wi-time-7:before {
+ content: "\f090";
+}
+.wi-time-8:before {
+ content: "\f091";
+}
+.wi-time-9:before {
+ content: "\f092";
+}
+.wi-time-10:before {
+ content: "\f093";
+}
+.wi-time-11:before {
+ content: "\f094";
+}
+.wi-time-12:before {
+ content: "\f089";
+}
+.wi-direction-up:before {
+ content: "\f058";
+}
+.wi-direction-up-right:before {
+ content: "\f057";
+}
+.wi-direction-right:before {
+ content: "\f04d";
+}
+.wi-direction-down-right:before {
+ content: "\f088";
+}
+.wi-direction-down:before {
+ content: "\f044";
+}
+.wi-direction-down-left:before {
+ content: "\f043";
+}
+.wi-direction-left:before {
+ content: "\f048";
+}
+.wi-direction-up-left:before {
+ content: "\f087";
+}
+.wi-wind-beaufort-0:before {
+ content: "\f0b7";
+}
+.wi-wind-beaufort-1:before {
+ content: "\f0b8";
+}
+.wi-wind-beaufort-2:before {
+ content: "\f0b9";
+}
+.wi-wind-beaufort-3:before {
+ content: "\f0ba";
+}
+.wi-wind-beaufort-4:before {
+ content: "\f0bb";
+}
+.wi-wind-beaufort-5:before {
+ content: "\f0bc";
+}
+.wi-wind-beaufort-6:before {
+ content: "\f0bd";
+}
+.wi-wind-beaufort-7:before {
+ content: "\f0be";
+}
+.wi-wind-beaufort-8:before {
+ content: "\f0bf";
+}
+.wi-wind-beaufort-9:before {
+ content: "\f0c0";
+}
+.wi-wind-beaufort-10:before {
+ content: "\f0c1";
+}
+.wi-wind-beaufort-11:before {
+ content: "\f0c2";
+}
+.wi-wind-beaufort-12:before {
+ content: "\f0c3";
+}
+.wi-yahoo-0:before {
+ content: "\f056";
+}
+.wi-yahoo-1:before {
+ content: "\f00e";
+}
+.wi-yahoo-2:before {
+ content: "\f073";
+}
+.wi-yahoo-3:before {
+ content: "\f01e";
+}
+.wi-yahoo-4:before {
+ content: "\f01e";
+}
+.wi-yahoo-5:before {
+ content: "\f017";
+}
+.wi-yahoo-6:before {
+ content: "\f017";
+}
+.wi-yahoo-7:before {
+ content: "\f017";
+}
+.wi-yahoo-8:before {
+ content: "\f015";
+}
+.wi-yahoo-9:before {
+ content: "\f01a";
+}
+.wi-yahoo-10:before {
+ content: "\f015";
+}
+.wi-yahoo-11:before {
+ content: "\f01a";
+}
+.wi-yahoo-12:before {
+ content: "\f01a";
+}
+.wi-yahoo-13:before {
+ content: "\f01b";
+}
+.wi-yahoo-14:before {
+ content: "\f00a";
+}
+.wi-yahoo-15:before {
+ content: "\f064";
+}
+.wi-yahoo-16:before {
+ content: "\f01b";
+}
+.wi-yahoo-17:before {
+ content: "\f015";
+}
+.wi-yahoo-18:before {
+ content: "\f017";
+}
+.wi-yahoo-19:before {
+ content: "\f063";
+}
+.wi-yahoo-20:before {
+ content: "\f014";
+}
+.wi-yahoo-21:before {
+ content: "\f021";
+}
+.wi-yahoo-22:before {
+ content: "\f062";
+}
+.wi-yahoo-23:before {
+ content: "\f050";
+}
+.wi-yahoo-24:before {
+ content: "\f050";
+}
+.wi-yahoo-25:before {
+ content: "\f076";
+}
+.wi-yahoo-26:before {
+ content: "\f013";
+}
+.wi-yahoo-27:before {
+ content: "\f031";
+}
+.wi-yahoo-28:before {
+ content: "\f002";
+}
+.wi-yahoo-29:before {
+ content: "\f031";
+}
+.wi-yahoo-30:before {
+ content: "\f002";
+}
+.wi-yahoo-31:before {
+ content: "\f02e";
+}
+.wi-yahoo-32:before {
+ content: "\f00d";
+}
+.wi-yahoo-33:before {
+ content: "\f083";
+}
+.wi-yahoo-34:before {
+ content: "\f00c";
+}
+.wi-yahoo-35:before {
+ content: "\f017";
+}
+.wi-yahoo-36:before {
+ content: "\f072";
+}
+.wi-yahoo-37:before {
+ content: "\f00e";
+}
+.wi-yahoo-38:before {
+ content: "\f00e";
+}
+.wi-yahoo-39:before {
+ content: "\f00e";
+}
+.wi-yahoo-40:before {
+ content: "\f01a";
+}
+.wi-yahoo-41:before {
+ content: "\f064";
+}
+.wi-yahoo-42:before {
+ content: "\f01b";
+}
+.wi-yahoo-43:before {
+ content: "\f064";
+}
+.wi-yahoo-44:before {
+ content: "\f00c";
+}
+.wi-yahoo-45:before {
+ content: "\f00e";
+}
+.wi-yahoo-46:before {
+ content: "\f01b";
+}
+.wi-yahoo-47:before {
+ content: "\f00e";
+}
+.wi-yahoo-3200:before {
+ content: "\f077";
+}
+.wi-forecast-io-clear-day:before {
+ content: "\f00d";
+}
+.wi-forecast-io-clear-night:before {
+ content: "\f02e";
+}
+.wi-forecast-io-rain:before {
+ content: "\f019";
+}
+.wi-forecast-io-snow:before {
+ content: "\f01b";
+}
+.wi-forecast-io-sleet:before {
+ content: "\f0b5";
+}
+.wi-forecast-io-wind:before {
+ content: "\f050";
+}
+.wi-forecast-io-fog:before {
+ content: "\f014";
+}
+.wi-forecast-io-cloudy:before {
+ content: "\f013";
+}
+.wi-forecast-io-partly-cloudy-day:before {
+ content: "\f002";
+}
+.wi-forecast-io-partly-cloudy-night:before {
+ content: "\f031";
+}
+.wi-forecast-io-hail:before {
+ content: "\f015";
+}
+.wi-forecast-io-thunderstorm:before {
+ content: "\f01e";
+}
+.wi-forecast-io-tornado:before {
+ content: "\f056";
+}
+.wi-wmo4680-0:before,
+.wi-wmo4680-00:before {
+ content: "\f055";
+}
+.wi-wmo4680-1:before,
+.wi-wmo4680-01:before {
+ content: "\f013";
+}
+.wi-wmo4680-2:before,
+.wi-wmo4680-02:before {
+ content: "\f055";
+}
+.wi-wmo4680-3:before,
+.wi-wmo4680-03:before {
+ content: "\f013";
+}
+.wi-wmo4680-4:before,
+.wi-wmo4680-04:before {
+ content: "\f014";
+}
+.wi-wmo4680-5:before,
+.wi-wmo4680-05:before {
+ content: "\f014";
+}
+.wi-wmo4680-10:before {
+ content: "\f014";
+}
+.wi-wmo4680-11:before {
+ content: "\f014";
+}
+.wi-wmo4680-12:before {
+ content: "\f016";
+}
+.wi-wmo4680-18:before {
+ content: "\f050";
+}
+.wi-wmo4680-20:before {
+ content: "\f014";
+}
+.wi-wmo4680-21:before {
+ content: "\f017";
+}
+.wi-wmo4680-22:before {
+ content: "\f017";
+}
+.wi-wmo4680-23:before {
+ content: "\f019";
+}
+.wi-wmo4680-24:before {
+ content: "\f01b";
+}
+.wi-wmo4680-25:before {
+ content: "\f015";
+}
+.wi-wmo4680-26:before {
+ content: "\f01e";
+}
+.wi-wmo4680-27:before {
+ content: "\f063";
+}
+.wi-wmo4680-28:before {
+ content: "\f063";
+}
+.wi-wmo4680-29:before {
+ content: "\f063";
+}
+.wi-wmo4680-30:before {
+ content: "\f014";
+}
+.wi-wmo4680-31:before {
+ content: "\f014";
+}
+.wi-wmo4680-32:before {
+ content: "\f014";
+}
+.wi-wmo4680-33:before {
+ content: "\f014";
+}
+.wi-wmo4680-34:before {
+ content: "\f014";
+}
+.wi-wmo4680-35:before {
+ content: "\f014";
+}
+.wi-wmo4680-40:before {
+ content: "\f017";
+}
+.wi-wmo4680-41:before {
+ content: "\f01c";
+}
+.wi-wmo4680-42:before {
+ content: "\f019";
+}
+.wi-wmo4680-43:before {
+ content: "\f01c";
+}
+.wi-wmo4680-44:before {
+ content: "\f019";
+}
+.wi-wmo4680-45:before {
+ content: "\f015";
+}
+.wi-wmo4680-46:before {
+ content: "\f015";
+}
+.wi-wmo4680-47:before {
+ content: "\f01b";
+}
+.wi-wmo4680-48:before {
+ content: "\f01b";
+}
+.wi-wmo4680-50:before {
+ content: "\f01c";
+}
+.wi-wmo4680-51:before {
+ content: "\f01c";
+}
+.wi-wmo4680-52:before {
+ content: "\f019";
+}
+.wi-wmo4680-53:before {
+ content: "\f019";
+}
+.wi-wmo4680-54:before {
+ content: "\f076";
+}
+.wi-wmo4680-55:before {
+ content: "\f076";
+}
+.wi-wmo4680-56:before {
+ content: "\f076";
+}
+.wi-wmo4680-57:before {
+ content: "\f01c";
+}
+.wi-wmo4680-58:before {
+ content: "\f019";
+}
+.wi-wmo4680-60:before {
+ content: "\f01c";
+}
+.wi-wmo4680-61:before {
+ content: "\f01c";
+}
+.wi-wmo4680-62:before {
+ content: "\f019";
+}
+.wi-wmo4680-63:before {
+ content: "\f019";
+}
+.wi-wmo4680-64:before {
+ content: "\f015";
+}
+.wi-wmo4680-65:before {
+ content: "\f015";
+}
+.wi-wmo4680-66:before {
+ content: "\f015";
+}
+.wi-wmo4680-67:before {
+ content: "\f017";
+}
+.wi-wmo4680-68:before {
+ content: "\f017";
+}
+.wi-wmo4680-70:before {
+ content: "\f01b";
+}
+.wi-wmo4680-71:before {
+ content: "\f01b";
+}
+.wi-wmo4680-72:before {
+ content: "\f01b";
+}
+.wi-wmo4680-73:before {
+ content: "\f01b";
+}
+.wi-wmo4680-74:before {
+ content: "\f076";
+}
+.wi-wmo4680-75:before {
+ content: "\f076";
+}
+.wi-wmo4680-76:before {
+ content: "\f076";
+}
+.wi-wmo4680-77:before {
+ content: "\f01b";
+}
+.wi-wmo4680-78:before {
+ content: "\f076";
+}
+.wi-wmo4680-80:before {
+ content: "\f019";
+}
+.wi-wmo4680-81:before {
+ content: "\f01c";
+}
+.wi-wmo4680-82:before {
+ content: "\f019";
+}
+.wi-wmo4680-83:before {
+ content: "\f019";
+}
+.wi-wmo4680-84:before {
+ content: "\f01d";
+}
+.wi-wmo4680-85:before {
+ content: "\f017";
+}
+.wi-wmo4680-86:before {
+ content: "\f017";
+}
+.wi-wmo4680-87:before {
+ content: "\f017";
+}
+.wi-wmo4680-89:before {
+ content: "\f015";
+}
+.wi-wmo4680-90:before {
+ content: "\f016";
+}
+.wi-wmo4680-91:before {
+ content: "\f01d";
+}
+.wi-wmo4680-92:before {
+ content: "\f01e";
+}
+.wi-wmo4680-93:before {
+ content: "\f01e";
+}
+.wi-wmo4680-94:before {
+ content: "\f016";
+}
+.wi-wmo4680-95:before {
+ content: "\f01e";
+}
+.wi-wmo4680-96:before {
+ content: "\f01e";
+}
+.wi-wmo4680-99:before {
+ content: "\f056";
+}
+.wi-owm-200:before {
+ content: "\f01e";
+}
+.wi-owm-201:before {
+ content: "\f01e";
+}
+.wi-owm-202:before {
+ content: "\f01e";
+}
+.wi-owm-210:before {
+ content: "\f016";
+}
+.wi-owm-211:before {
+ content: "\f016";
+}
+.wi-owm-212:before {
+ content: "\f016";
+}
+.wi-owm-221:before {
+ content: "\f016";
+}
+.wi-owm-230:before {
+ content: "\f01e";
+}
+.wi-owm-231:before {
+ content: "\f01e";
+}
+.wi-owm-232:before {
+ content: "\f01e";
+}
+.wi-owm-300:before {
+ content: "\f01c";
+}
+.wi-owm-301:before {
+ content: "\f01c";
+}
+.wi-owm-302:before {
+ content: "\f019";
+}
+.wi-owm-310:before {
+ content: "\f017";
+}
+.wi-owm-311:before {
+ content: "\f019";
+}
+.wi-owm-312:before {
+ content: "\f019";
+}
+.wi-owm-313:before {
+ content: "\f01a";
+}
+.wi-owm-314:before {
+ content: "\f019";
+}
+.wi-owm-321:before {
+ content: "\f01c";
+}
+.wi-owm-500:before {
+ content: "\f01c";
+}
+.wi-owm-501:before {
+ content: "\f019";
+}
+.wi-owm-502:before {
+ content: "\f019";
+}
+.wi-owm-503:before {
+ content: "\f019";
+}
+.wi-owm-504:before {
+ content: "\f019";
+}
+.wi-owm-511:before {
+ content: "\f017";
+}
+.wi-owm-520:before {
+ content: "\f01a";
+}
+.wi-owm-521:before {
+ content: "\f01a";
+}
+.wi-owm-522:before {
+ content: "\f01a";
+}
+.wi-owm-531:before {
+ content: "\f01d";
+}
+.wi-owm-600:before {
+ content: "\f01b";
+}
+.wi-owm-601:before {
+ content: "\f01b";
+}
+.wi-owm-602:before {
+ content: "\f0b5";
+}
+.wi-owm-611:before {
+ content: "\f017";
+}
+.wi-owm-612:before {
+ content: "\f017";
+}
+.wi-owm-615:before {
+ content: "\f017";
+}
+.wi-owm-616:before {
+ content: "\f017";
+}
+.wi-owm-620:before {
+ content: "\f017";
+}
+.wi-owm-621:before {
+ content: "\f01b";
+}
+.wi-owm-622:before {
+ content: "\f01b";
+}
+.wi-owm-701:before {
+ content: "\f014";
+}
+.wi-owm-711:before {
+ content: "\f062";
+}
+.wi-owm-721:before {
+ content: "\f0b6";
+}
+.wi-owm-731:before {
+ content: "\f063";
+}
+.wi-owm-741:before {
+ content: "\f014";
+}
+.wi-owm-761:before {
+ content: "\f063";
+}
+.wi-owm-762:before {
+ content: "\f063";
+}
+.wi-owm-771:before {
+ content: "\f011";
+}
+.wi-owm-781:before {
+ content: "\f056";
+}
+.wi-owm-800:before {
+ content: "\f00d";
+}
+.wi-owm-801:before {
+ content: "\f041";
+}
+.wi-owm-802:before {
+ content: "\f041";
+}
+.wi-owm-803:before {
+ content: "\f013";
+}
+.wi-owm-804:before {
+ content: "\f013";
+}
+.wi-owm-900:before {
+ content: "\f056";
+}
+.wi-owm-901:before {
+ content: "\f01d";
+}
+.wi-owm-902:before {
+ content: "\f073";
+}
+.wi-owm-903:before {
+ content: "\f076";
+}
+.wi-owm-904:before {
+ content: "\f072";
+}
+.wi-owm-905:before {
+ content: "\f021";
+}
+.wi-owm-906:before {
+ content: "\f015";
+}
+.wi-owm-957:before {
+ content: "\f050";
+}
+.wi-owm-day-200:before {
+ content: "\f010";
+}
+.wi-owm-day-201:before {
+ content: "\f010";
+}
+.wi-owm-day-202:before {
+ content: "\f010";
+}
+.wi-owm-day-210:before {
+ content: "\f005";
+}
+.wi-owm-day-211:before {
+ content: "\f005";
+}
+.wi-owm-day-212:before {
+ content: "\f005";
+}
+.wi-owm-day-221:before {
+ content: "\f005";
+}
+.wi-owm-day-230:before {
+ content: "\f010";
+}
+.wi-owm-day-231:before {
+ content: "\f010";
+}
+.wi-owm-day-232:before {
+ content: "\f010";
+}
+.wi-owm-day-300:before {
+ content: "\f00b";
+}
+.wi-owm-day-301:before {
+ content: "\f00b";
+}
+.wi-owm-day-302:before {
+ content: "\f008";
+}
+.wi-owm-day-310:before {
+ content: "\f008";
+}
+.wi-owm-day-311:before {
+ content: "\f008";
+}
+.wi-owm-day-312:before {
+ content: "\f008";
+}
+.wi-owm-day-313:before {
+ content: "\f008";
+}
+.wi-owm-day-314:before {
+ content: "\f008";
+}
+.wi-owm-day-321:before {
+ content: "\f00b";
+}
+.wi-owm-day-500:before {
+ content: "\f00b";
+}
+.wi-owm-day-501:before {
+ content: "\f008";
+}
+.wi-owm-day-502:before {
+ content: "\f008";
+}
+.wi-owm-day-503:before {
+ content: "\f008";
+}
+.wi-owm-day-504:before {
+ content: "\f008";
+}
+.wi-owm-day-511:before {
+ content: "\f006";
+}
+.wi-owm-day-520:before {
+ content: "\f009";
+}
+.wi-owm-day-521:before {
+ content: "\f009";
+}
+.wi-owm-day-522:before {
+ content: "\f009";
+}
+.wi-owm-day-531:before {
+ content: "\f00e";
+}
+.wi-owm-day-600:before {
+ content: "\f00a";
+}
+.wi-owm-day-601:before {
+ content: "\f0b2";
+}
+.wi-owm-day-602:before {
+ content: "\f00a";
+}
+.wi-owm-day-611:before {
+ content: "\f006";
+}
+.wi-owm-day-612:before {
+ content: "\f006";
+}
+.wi-owm-day-615:before {
+ content: "\f006";
+}
+.wi-owm-day-616:before {
+ content: "\f006";
+}
+.wi-owm-day-620:before {
+ content: "\f006";
+}
+.wi-owm-day-621:before {
+ content: "\f00a";
+}
+.wi-owm-day-622:before {
+ content: "\f00a";
+}
+.wi-owm-day-701:before {
+ content: "\f003";
+}
+.wi-owm-day-711:before {
+ content: "\f062";
+}
+.wi-owm-day-721:before {
+ content: "\f0b6";
+}
+.wi-owm-day-731:before {
+ content: "\f063";
+}
+.wi-owm-day-741:before {
+ content: "\f003";
+}
+.wi-owm-day-761:before {
+ content: "\f063";
+}
+.wi-owm-day-762:before {
+ content: "\f063";
+}
+.wi-owm-day-781:before {
+ content: "\f056";
+}
+.wi-owm-day-800:before {
+ content: "\f00d";
+}
+.wi-owm-day-801:before {
+ content: "\f002";
+}
+.wi-owm-day-802:before {
+ content: "\f002";
+}
+.wi-owm-day-803:before {
+ content: "\f013";
+}
+.wi-owm-day-804:before {
+ content: "\f013";
+}
+.wi-owm-day-900:before {
+ content: "\f056";
+}
+.wi-owm-day-902:before {
+ content: "\f073";
+}
+.wi-owm-day-903:before {
+ content: "\f076";
+}
+.wi-owm-day-904:before {
+ content: "\f072";
+}
+.wi-owm-day-906:before {
+ content: "\f004";
+}
+.wi-owm-day-957:before {
+ content: "\f050";
+}
+.wi-owm-night-200:before {
+ content: "\f02d";
+}
+.wi-owm-night-201:before {
+ content: "\f02d";
+}
+.wi-owm-night-202:before {
+ content: "\f02d";
+}
+.wi-owm-night-210:before {
+ content: "\f025";
+}
+.wi-owm-night-211:before {
+ content: "\f025";
+}
+.wi-owm-night-212:before {
+ content: "\f025";
+}
+.wi-owm-night-221:before {
+ content: "\f025";
+}
+.wi-owm-night-230:before {
+ content: "\f02d";
+}
+.wi-owm-night-231:before {
+ content: "\f02d";
+}
+.wi-owm-night-232:before {
+ content: "\f02d";
+}
+.wi-owm-night-300:before {
+ content: "\f02b";
+}
+.wi-owm-night-301:before {
+ content: "\f02b";
+}
+.wi-owm-night-302:before {
+ content: "\f028";
+}
+.wi-owm-night-310:before {
+ content: "\f028";
+}
+.wi-owm-night-311:before {
+ content: "\f028";
+}
+.wi-owm-night-312:before {
+ content: "\f028";
+}
+.wi-owm-night-313:before {
+ content: "\f028";
+}
+.wi-owm-night-314:before {
+ content: "\f028";
+}
+.wi-owm-night-321:before {
+ content: "\f02b";
+}
+.wi-owm-night-500:before {
+ content: "\f02b";
+}
+.wi-owm-night-501:before {
+ content: "\f028";
+}
+.wi-owm-night-502:before {
+ content: "\f028";
+}
+.wi-owm-night-503:before {
+ content: "\f028";
+}
+.wi-owm-night-504:before {
+ content: "\f028";
+}
+.wi-owm-night-511:before {
+ content: "\f026";
+}
+.wi-owm-night-520:before {
+ content: "\f029";
+}
+.wi-owm-night-521:before {
+ content: "\f029";
+}
+.wi-owm-night-522:before {
+ content: "\f029";
+}
+.wi-owm-night-531:before {
+ content: "\f02c";
+}
+.wi-owm-night-600:before {
+ content: "\f02a";
+}
+.wi-owm-night-601:before {
+ content: "\f0b4";
+}
+.wi-owm-night-602:before {
+ content: "\f02a";
+}
+.wi-owm-night-611:before {
+ content: "\f026";
+}
+.wi-owm-night-612:before {
+ content: "\f026";
+}
+.wi-owm-night-615:before {
+ content: "\f026";
+}
+.wi-owm-night-616:before {
+ content: "\f026";
+}
+.wi-owm-night-620:before {
+ content: "\f026";
+}
+.wi-owm-night-621:before {
+ content: "\f02a";
+}
+.wi-owm-night-622:before {
+ content: "\f02a";
+}
+.wi-owm-night-701:before {
+ content: "\f04a";
+}
+.wi-owm-night-711:before {
+ content: "\f062";
+}
+.wi-owm-night-721:before {
+ content: "\f0b6";
+}
+.wi-owm-night-731:before {
+ content: "\f063";
+}
+.wi-owm-night-741:before {
+ content: "\f04a";
+}
+.wi-owm-night-761:before {
+ content: "\f063";
+}
+.wi-owm-night-762:before {
+ content: "\f063";
+}
+.wi-owm-night-781:before {
+ content: "\f056";
+}
+.wi-owm-night-800:before {
+ content: "\f02e";
+}
+.wi-owm-night-801:before {
+ content: "\f081";
+}
+.wi-owm-night-802:before {
+ content: "\f086";
+}
+.wi-owm-night-803:before {
+ content: "\f013";
+}
+.wi-owm-night-804:before {
+ content: "\f013";
+}
+.wi-owm-night-900:before {
+ content: "\f056";
+}
+.wi-owm-night-902:before {
+ content: "\f073";
+}
+.wi-owm-night-903:before {
+ content: "\f076";
+}
+.wi-owm-night-904:before {
+ content: "\f072";
+}
+.wi-owm-night-906:before {
+ content: "\f024";
+}
+.wi-owm-night-957:before {
+ content: "\f050";
+}
+.wi-wu-chanceflurries:before {
+ content: "\f064";
+}
+.wi-wu-chancerain:before {
+ content: "\f019";
+}
+.wi-wu-chancesleat:before {
+ content: "\f0b5";
+}
+.wi-wu-chancesnow:before {
+ content: "\f01b";
+}
+.wi-wu-chancetstorms:before {
+ content: "\f01e";
+}
+.wi-wu-clear:before {
+ content: "\f00d";
+}
+.wi-wu-cloudy:before {
+ content: "\f002";
+}
+.wi-wu-flurries:before {
+ content: "\f064";
+}
+.wi-wu-hazy:before {
+ content: "\f0b6";
+}
+.wi-wu-mostlycloudy:before {
+ content: "\f002";
+}
+.wi-wu-mostlysunny:before {
+ content: "\f00d";
+}
+.wi-wu-partlycloudy:before {
+ content: "\f002";
+}
+.wi-wu-partlysunny:before {
+ content: "\f00d";
+}
+.wi-wu-rain:before {
+ content: "\f01a";
+}
+.wi-wu-sleat:before {
+ content: "\f0b5";
+}
+.wi-wu-snow:before {
+ content: "\f01b";
+}
+.wi-wu-sunny:before {
+ content: "\f00d";
+}
+.wi-wu-tstorms:before {
+ content: "\f01e";
+}
+.wi-wu-unknown:before {
+ content: "\f00d";
+}
diff --git a/src/utils/cached-fetch.js b/src/utils/cached-fetch.js
new file mode 100644
index 00000000..88372d53
--- /dev/null
+++ b/src/utils/cached-fetch.js
@@ -0,0 +1,13 @@
+import cache from "memory-cache";
+
+export default async function cachedFetch(url, duration) {
+ const cached = cache.get(url);
+
+ if (cached) {
+ return cached;
+ } else {
+ const data = await fetch(url).then((res) => res.json());
+ cache.put(url, data, duration * 1000 * 60);
+ return data;
+ }
+}
diff --git a/src/utils/condition-map.js b/src/utils/condition-map.js
new file mode 100644
index 00000000..8ab5f25b
--- /dev/null
+++ b/src/utils/condition-map.js
@@ -0,0 +1,356 @@
+import * as Icons from "react-icons/wi";
+
+const conditions = [
+ {
+ code: 1000,
+ icon: {
+ day: Icons.WiDaySunny,
+ night: Icons.WiNightClear,
+ },
+ },
+ {
+ code: 1003,
+ icon: {
+ day: Icons.WiDayCloudy,
+ night: Icons.WiNightPartlyCloudy,
+ },
+ },
+ {
+ code: 1006,
+ icon: {
+ day: Icons.WiDayCloudy,
+ night: Icons.WiNightCloudy,
+ },
+ },
+ {
+ code: 1009,
+ icon: {
+ day: Icons.WiDayCloudy,
+ night: Icons.WiNightCloudy,
+ },
+ },
+ {
+ code: 1030,
+ icon: {
+ day: Icons.WiDayFog,
+ night: Icons.WiNightFog,
+ },
+ },
+ {
+ code: 1063,
+ icon: {
+ day: Icons.WiDayRain,
+ night: Icons.WiNightRain,
+ },
+ },
+ {
+ code: 1066,
+ icon: {
+ day: Icons.WiDaySnow,
+ night: Icons.WiNightSnow,
+ },
+ },
+ {
+ code: 1069,
+ icon: {
+ day: Icons.WiDayRainMix,
+ night: Icons.WiNightRainMix,
+ },
+ },
+ {
+ code: 1072,
+ icon: {
+ day: Icons.WiDaySleet,
+ night: Icons.WiNightSleet,
+ },
+ },
+ {
+ code: 1087,
+ icon: {
+ day: Icons.WiDayThunderstorm,
+ night: Icons.WiNightThunderstorm,
+ },
+ },
+ {
+ code: 1114,
+ icon: {
+ day: Icons.WiDaySnow,
+ night: Icons.WiNightSnow,
+ },
+ },
+ {
+ code: 1117,
+ icon: {
+ day: Icons.WiDaySnow,
+ night: Icons.WiNightSnow,
+ },
+ },
+ {
+ code: 1135,
+ icon: {
+ day: Icons.WiDayFog,
+ night: Icons.WiNightFog,
+ },
+ },
+ {
+ code: 1147,
+ icon: {
+ day: Icons.WiDayFog,
+ night: Icons.WiNightFog,
+ },
+ },
+ {
+ code: 1150,
+ icon: {
+ day: Icons.WiDayRain,
+ night: Icons.WiNightRain,
+ },
+ },
+ {
+ code: 1153,
+ icon: {
+ day: Icons.WiDayRain,
+ night: Icons.WiNightRain,
+ },
+ },
+ {
+ code: 1168,
+ icon: {
+ day: Icons.WiDaySleet,
+ night: Icons.WiNightSleet,
+ },
+ },
+ {
+ code: 1171,
+ icon: {
+ day: Icons.WiDaySleet,
+ night: Icons.WiNightSleet,
+ },
+ },
+ {
+ code: 1180,
+ icon: {
+ day: Icons.WiDayRain,
+ night: Icons.WiNightRain,
+ },
+ },
+ {
+ code: 1183,
+ icon: {
+ day: Icons.WiDayRain,
+ night: Icons.WiNightRain,
+ },
+ },
+ {
+ code: 1186,
+ icon: {
+ day: Icons.WiDayRain,
+ night: Icons.WiNightRain,
+ },
+ },
+ {
+ code: 1189,
+ icon: {
+ day: Icons.WiDayRain,
+ night: Icons.WiNightRain,
+ },
+ },
+ {
+ code: 1192,
+ icon: {
+ day: Icons.WiDayRain,
+ night: Icons.WiNightRain,
+ },
+ },
+ {
+ code: 1195,
+ icon: {
+ day: Icons.WiDayRain,
+ night: Icons.WiNightRain,
+ },
+ },
+ {
+ code: 1198,
+ icon: {
+ day: Icons.WiDaySleet,
+ night: Icons.WiNightSleet,
+ },
+ },
+ {
+ code: 1201,
+ icon: {
+ day: Icons.WiDaySleet,
+ night: Icons.WiNightSleet,
+ },
+ },
+ {
+ code: 1204,
+ icon: {
+ day: Icons.WiDayRainMix,
+ night: Icons.WiNightRainMix,
+ },
+ },
+ {
+ code: 1207,
+ icon: {
+ day: Icons.WiDayRainMix,
+ night: Icons.WiNightRainMix,
+ },
+ },
+ {
+ code: 1210,
+ icon: {
+ day: Icons.WiDaySnow,
+ night: Icons.WiNightSnow,
+ },
+ },
+ {
+ code: 1213,
+ icon: {
+ day: Icons.WiDaySnow,
+ night: Icons.WiNightSnow,
+ },
+ },
+ {
+ code: 1216,
+ icon: {
+ day: Icons.WiDaySnow,
+ night: Icons.WiNightSnow,
+ },
+ },
+ {
+ code: 1219,
+ icon: {
+ day: Icons.WiDaySnow,
+ night: Icons.WiNightSnow,
+ },
+ },
+ {
+ code: 1222,
+ icon: {
+ day: Icons.WiDaySnow,
+ night: Icons.WiNightSnow,
+ },
+ },
+ {
+ code: 1225,
+ icon: {
+ day: Icons.WiDaySnow,
+ night: Icons.WiNightSnow,
+ },
+ },
+ {
+ code: 1237,
+ icon: {
+ day: Icons.WiDayHail,
+ night: Icons.WiNightHail,
+ },
+ },
+ {
+ code: 1240,
+ icon: {
+ day: Icons.WiDayRain,
+ night: Icons.WiNightRain,
+ },
+ },
+ {
+ code: 1243,
+ icon: {
+ day: Icons.WiDayRain,
+ night: Icons.WiNightRain,
+ },
+ },
+ {
+ code: 1246,
+ icon: {
+ day: Icons.WiDayRain,
+ night: Icons.WiNightRain,
+ },
+ },
+ {
+ code: 1249,
+ icon: {
+ day: Icons.WiDayRainMix,
+ night: Icons.WiNightRainMix,
+ },
+ },
+ {
+ code: 1252,
+ icon: {
+ day: Icons.WiDayRainMix,
+ night: Icons.WiNightRainMix,
+ },
+ },
+ {
+ code: 1255,
+ icon: {
+ day: Icons.WiDaySnow,
+ night: Icons.WiNightSnow,
+ },
+ },
+ {
+ code: 1258,
+ icon: {
+ day: Icons.WiDaySnow,
+ night: Icons.WiNightSnow,
+ },
+ },
+ {
+ code: 1261,
+ icon: {
+ day: Icons.WiDayHail,
+ night: Icons.WiNightHail,
+ },
+ },
+ {
+ code: 1264,
+ icon: {
+ day: Icons.WiDayHail,
+ night: Icons.WiNightHail,
+ },
+ },
+ {
+ code: 1273,
+ icon: {
+ day: Icons.WiDayThunderstorm,
+ night: Icons.WiNightThunderstorm,
+ },
+ },
+ {
+ code: 1276,
+ icon: {
+ day: Icons.WiDayThunderstorm,
+ night: Icons.WiNightThunderstorm,
+ },
+ },
+ {
+ code: 1279,
+ icon: {
+ day: Icons.WiDayThunderstorm,
+ night: Icons.WiNightThunderstorm,
+ },
+ },
+ {
+ code: 1282,
+ icon: {
+ day: Icons.WiDayThunderstorm,
+ night: Icons.WiNightThunderstorm,
+ },
+ },
+];
+
+export default function mapIcon(weatherStatusCode, timeOfDay) {
+ const mapping = conditions.find(
+ (condition) => condition.code === weatherStatusCode
+ );
+
+ if (mapping) {
+ if (timeOfDay === "day") {
+ return mapping.icon.day;
+ } else if (timeOfDay === "night") {
+ return mapping.icon.night;
+ }
+ }
+
+ return Icons.WiDaySunny;
+}
diff --git a/src/utils/config.js b/src/utils/config.js
new file mode 100644
index 00000000..7fe9dc3e
--- /dev/null
+++ b/src/utils/config.js
@@ -0,0 +1,13 @@
+import { join } from "path";
+import { existsSync, copyFile } from "fs";
+
+export default function checkAndCopyConfig(config) {
+ const configYaml = join(process.cwd(), "config", config);
+ if (!existsSync(configYaml)) {
+ const configSkeleton = join(process.cwd(), "src", "skeleton", config);
+ copyFile(configSkeleton, configYaml, (err) => {
+ if (err) throw err;
+ console.info("%s was copied to the config folder", config);
+ });
+ }
+}
diff --git a/src/utils/docker.js b/src/utils/docker.js
new file mode 100644
index 00000000..9d2d4646
--- /dev/null
+++ b/src/utils/docker.js
@@ -0,0 +1,30 @@
+import yaml from "js-yaml";
+import path from "path";
+import { promises as fs } from "fs";
+import checkAndCopyConfig from "utils/config";
+
+export default async function getDockerArguments(server) {
+ checkAndCopyConfig("docker.yaml");
+
+ const configFile = path.join(process.cwd(), "config", "docker.yaml");
+ const configData = await fs.readFile(configFile, "utf8");
+ const servers = yaml.load(configData);
+
+ if (!server) {
+ if (process.platform !== "win32" && process.platform !== "darwin") {
+ return { socketPath: "/var/run/docker.sock" };
+ } else {
+ return { host: "127.0.0.1" };
+ }
+ } else if (servers[server]) {
+ if (servers[server].socket) {
+ return { socketPath: servers[server].socket };
+ } else if (servers[server].host) {
+ return { host: servers[server].host, port: servers[server].port || null };
+ } else {
+ return servers[server];
+ }
+ } else {
+ return null;
+ }
+}
diff --git a/src/utils/stats-helpers.js b/src/utils/stats-helpers.js
new file mode 100644
index 00000000..4b21b4cd
--- /dev/null
+++ b/src/utils/stats-helpers.js
@@ -0,0 +1,26 @@
+export function calculateCPUPercent(stats) {
+ let cpuPercent = 0.0;
+ const cpuDelta =
+ stats.cpu_stats.cpu_usage.total_usage -
+ stats.precpu_stats.cpu_usage.total_usage;
+ const systemDelta =
+ stats.cpu_stats.system_cpu_usage - stats.precpu_stats.system_cpu_usage;
+
+ if (systemDelta > 0.0 && cpuDelta > 0.0) {
+ cpuPercent = (cpuDelta / systemDelta) * stats.cpu_stats.online_cpus * 100.0;
+ }
+
+ return Math.round(cpuPercent * 10) / 10;
+}
+
+export function formatBytes(bytes, decimals = 2) {
+ if (bytes === 0) return "0 Bytes";
+
+ const k = 1024;
+ const dm = decimals < 0 ? 0 : decimals;
+ const sizes = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
+
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
+
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
+}
diff --git a/src/utils/theme-context.js b/src/utils/theme-context.js
new file mode 100644
index 00000000..49761633
--- /dev/null
+++ b/src/utils/theme-context.js
@@ -0,0 +1,47 @@
+import { createContext, useState, useEffect } from "react";
+
+const getInitialTheme = () => {
+ if (typeof window !== "undefined" && window.localStorage) {
+ const storedPrefs = window.localStorage.getItem("color-theme");
+ if (typeof storedPrefs === "string") {
+ return storedPrefs;
+ }
+
+ const userMedia = window.matchMedia("(prefers-color-scheme: dark)");
+ if (userMedia.matches) {
+ return "dark";
+ }
+ }
+
+ return "light"; // light theme as the default;
+};
+
+export const ThemeContext = createContext();
+
+export const ThemeProvider = ({ initialTheme, children }) => {
+ const [theme, setTheme] = useState(getInitialTheme);
+
+ const rawSetTheme = (rawTheme) => {
+ const root = window.document.documentElement;
+ const isDark = rawTheme === "dark";
+
+ root.classList.remove(isDark ? "light" : "dark");
+ root.classList.add(rawTheme);
+
+ localStorage.setItem("color-theme", rawTheme);
+ };
+
+ if (initialTheme) {
+ rawSetTheme(initialTheme);
+ }
+
+ useEffect(() => {
+ rawSetTheme(theme);
+ }, [theme]);
+
+ return (
+
+ {children}
+
+ );
+};
diff --git a/styles/Home.module.css b/styles/Home.module.css
deleted file mode 100644
index bd50f42f..00000000
--- a/styles/Home.module.css
+++ /dev/null
@@ -1,129 +0,0 @@
-.container {
- padding: 0 2rem;
-}
-
-.main {
- min-height: 100vh;
- padding: 4rem 0;
- flex: 1;
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
-}
-
-.footer {
- display: flex;
- flex: 1;
- padding: 2rem 0;
- border-top: 1px solid #eaeaea;
- justify-content: center;
- align-items: center;
-}
-
-.footer a {
- display: flex;
- justify-content: center;
- align-items: center;
- flex-grow: 1;
-}
-
-.title a {
- color: #0070f3;
- text-decoration: none;
-}
-
-.title a:hover,
-.title a:focus,
-.title a:active {
- text-decoration: underline;
-}
-
-.title {
- margin: 0;
- line-height: 1.15;
- font-size: 4rem;
-}
-
-.title,
-.description {
- text-align: center;
-}
-
-.description {
- margin: 4rem 0;
- line-height: 1.5;
- font-size: 1.5rem;
-}
-
-.code {
- background: #fafafa;
- border-radius: 5px;
- padding: 0.75rem;
- font-size: 1.1rem;
- font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
- Bitstream Vera Sans Mono, Courier New, monospace;
-}
-
-.grid {
- display: flex;
- align-items: center;
- justify-content: center;
- flex-wrap: wrap;
- max-width: 800px;
-}
-
-.card {
- margin: 1rem;
- padding: 1.5rem;
- text-align: left;
- color: inherit;
- text-decoration: none;
- border: 1px solid #eaeaea;
- border-radius: 10px;
- transition: color 0.15s ease, border-color 0.15s ease;
- max-width: 300px;
-}
-
-.card:hover,
-.card:focus,
-.card:active {
- color: #0070f3;
- border-color: #0070f3;
-}
-
-.card h2 {
- margin: 0 0 1rem 0;
- font-size: 1.5rem;
-}
-
-.card p {
- margin: 0;
- font-size: 1.25rem;
- line-height: 1.5;
-}
-
-.logo {
- height: 1em;
- margin-left: 0.5rem;
-}
-
-@media (max-width: 600px) {
- .grid {
- width: 100%;
- flex-direction: column;
- }
-}
-
-@media (prefers-color-scheme: dark) {
- .card,
- .footer {
- border-color: #222;
- }
- .code {
- background: #111;
- }
- .logo img {
- filter: invert(1);
- }
-}
diff --git a/styles/globals.css b/styles/globals.css
deleted file mode 100644
index 4f184216..00000000
--- a/styles/globals.css
+++ /dev/null
@@ -1,26 +0,0 @@
-html,
-body {
- padding: 0;
- margin: 0;
- font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
- Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
-}
-
-a {
- color: inherit;
- text-decoration: none;
-}
-
-* {
- box-sizing: border-box;
-}
-
-@media (prefers-color-scheme: dark) {
- html {
- color-scheme: dark;
- }
- body {
- color: white;
- background: black;
- }
-}
diff --git a/tailwind.config.js b/tailwind.config.js
new file mode 100644
index 00000000..b81ca6d2
--- /dev/null
+++ b/tailwind.config.js
@@ -0,0 +1,16 @@
+/** @type {import('tailwindcss').Config} */
+module.exports = {
+ darkMode: "class",
+ content: [
+ "./src/pages/**/*.{js,ts,jsx,tsx}",
+ "./src/components/**/*.{js,ts,jsx,tsx}",
+ ],
+ theme: {
+ extend: {
+ colors: ({ colors }) => ({
+ theme: colors.slate,
+ }),
+ },
+ },
+ plugins: [require("@tailwindcss/forms")],
+};