Compare commits
302 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ea279856f | ||
|
|
c7d1c05e6e | ||
|
|
48a09e5a99 | ||
|
|
dced918804 | ||
|
|
9dd259654a | ||
|
|
8f644ef7a4 | ||
|
|
61e82e2b2c | ||
|
|
17ec656b9b | ||
|
|
9669c463da | ||
|
|
02dd6d08b4 | ||
|
|
a8bd84467c | ||
|
|
770d52cbae | ||
|
|
73a200994c | ||
|
|
c1d14e3b16 | ||
|
|
e7215feee1 | ||
|
|
be4d02f5e9 | ||
|
|
92e47c45f9 | ||
|
|
73b44c3a2e | ||
|
|
bf666b3ec4 | ||
|
|
69a67f450d | ||
|
|
73e1d3dc20 | ||
|
|
95f811e4c1 | ||
|
|
65447f2b97 | ||
|
|
b6dd2975e7 | ||
|
|
911bd31c14 | ||
|
|
072e29025e | ||
|
|
7bb8274b95 | ||
|
|
280c8b0147 | ||
|
|
46a29ea7d6 | ||
|
|
b259ecf870 | ||
|
|
c1a55ff6d3 | ||
|
|
7c39cd8960 | ||
|
|
2bdd9eaa88 | ||
|
|
8e2ff61f1c | ||
|
|
99b70f96e4 | ||
|
|
802fe0f721 | ||
|
|
080bc44a6f | ||
|
|
321efd08cc | ||
|
|
37fed0ce01 | ||
|
|
778261f67e | ||
|
|
c95422b682 | ||
|
|
248c18d978 | ||
|
|
add17be5ba | ||
|
|
c7b5ec33a8 | ||
|
|
0625ce2bb9 | ||
|
|
860ae7f0f7 | ||
|
|
094b916c9e | ||
|
|
f50130971d | ||
|
|
05183a8861 | ||
|
|
92ffb8d082 | ||
|
|
9d79e7e72d | ||
|
|
6b201028cd | ||
|
|
46e0732bd1 | ||
|
|
375e513246 | ||
|
|
2c80dc63d1 | ||
|
|
d8a4d1ef5d | ||
|
|
2e4e998654 | ||
|
|
5b66941774 | ||
|
|
6f34ca50e0 | ||
|
|
59af5e1eb8 | ||
|
|
b34a44cf9f | ||
|
|
45c92ef2f7 | ||
|
|
8854fcdb9b | ||
|
|
8299c6ce8d | ||
|
|
d5861ea52b | ||
|
|
855665689c | ||
|
|
a4795c21dc | ||
|
|
2e2aeef77b | ||
|
|
5198b056cc | ||
|
|
3c23e59a70 | ||
|
|
2440da8e08 | ||
|
|
91795f2d07 | ||
|
|
9f55ec9a63 | ||
|
|
efeaa995e2 | ||
|
|
1524a6a6cf | ||
|
|
702ede8b9b | ||
|
|
7e6fcd8418 | ||
|
|
1f006a4093 | ||
|
|
d4a5245d1a | ||
|
|
60519a2881 | ||
|
|
182b66b53f | ||
|
|
4b110cf7a8 | ||
|
|
dc0744f9d7 | ||
|
|
90cd18735a | ||
|
|
494b2ebd08 | ||
|
|
797b966267 | ||
|
|
b13754f174 | ||
|
|
c9e7845154 | ||
|
|
a6f7c48cc3 | ||
|
|
7874eb1467 | ||
|
|
69626157ed | ||
|
|
6dc0eac9f8 | ||
|
|
a6fc171539 | ||
|
|
a8a715dd30 | ||
|
|
4ac6e43343 | ||
|
|
13ee38bd12 | ||
|
|
d186ff16dd | ||
|
|
49a3f3e249 | ||
|
|
2d14230763 | ||
|
|
ce86d06006 | ||
|
|
c1d476997b | ||
|
|
a79e9cd342 | ||
|
|
260201c2b4 | ||
|
|
85df467fdb | ||
|
|
04da8f3925 | ||
|
|
86b12debc5 | ||
|
|
69c9a449b1 | ||
|
|
0c20040f57 | ||
|
|
dedd725fae | ||
|
|
fe7fa5c060 | ||
|
|
d2dc51d49c | ||
|
|
d26ae30fa6 | ||
|
|
fbac27f504 | ||
|
|
d1b6dad14d | ||
|
|
fe1064b173 | ||
|
|
42da3eca28 | ||
|
|
7ee3113d8a | ||
|
|
2bd9c8eddc | ||
|
|
1249ecaa68 | ||
|
|
ba428cf3ae | ||
|
|
f06be8ca94 | ||
|
|
94a518f4a8 | ||
|
|
e56dccc7f1 | ||
|
|
e03822df6e | ||
|
|
ad1d1e751d | ||
|
|
ac4dcd3222 | ||
|
|
952f0295cc | ||
|
|
5ba75bc62d | ||
|
|
b9b9449cd3 | ||
|
|
adf601c572 | ||
|
|
00eb768cdf | ||
|
|
b8d00f24f7 | ||
|
|
031f11c5c1 | ||
|
|
1ffe7da9b7 | ||
|
|
292c6dd6b0 | ||
|
|
6f5a0ea6b8 | ||
|
|
f44b564bb5 | ||
|
|
b380fd2306 | ||
|
|
528ce1a5fe | ||
|
|
593d8a1cbc | ||
|
|
8f6ea7b49f | ||
|
|
01d005dbbe | ||
|
|
45fb059e71 | ||
|
|
40a11bd9ce | ||
|
|
935f97415f | ||
|
|
969dab9971 | ||
|
|
193b548d0b | ||
|
|
6dacd1bdd6 | ||
|
|
6a0a872790 | ||
|
|
fa71586a50 | ||
|
|
65c5828456 | ||
|
|
892c68856f | ||
|
|
ea96999377 | ||
|
|
a9fb458f19 | ||
|
|
88c774339d | ||
|
|
962e6e576c | ||
|
|
07a28c0841 | ||
|
|
1e7ef54c05 | ||
|
|
701270b020 | ||
|
|
84f142683f | ||
|
|
7033652508 | ||
|
|
6c3489aa3d | ||
|
|
82f18c7cff | ||
|
|
875eefe71f | ||
|
|
1972f2b6db | ||
|
|
dd080d9a04 | ||
|
|
933414934c | ||
|
|
dec25762f0 | ||
|
|
a2c9754560 | ||
|
|
0f107d8648 | ||
|
|
1b7b6af84d | ||
|
|
5e38e71229 | ||
|
|
83983d772d | ||
|
|
3e133c10d2 | ||
|
|
6a67873c10 | ||
|
|
588ea9b04e | ||
|
|
68b7fe2b35 | ||
|
|
5643af9845 | ||
|
|
ad3752650b | ||
|
|
8718b4bcee | ||
|
|
f29154cfa4 | ||
|
|
da948f83a1 | ||
|
|
84bb98b007 | ||
|
|
c3a623c329 | ||
|
|
1249724f8a | ||
|
|
4503612bf0 | ||
|
|
9c7a9eb326 | ||
|
|
07e99768e6 | ||
|
|
a63f71d3ee | ||
|
|
76f6b3a4a7 | ||
|
|
7052951a43 | ||
|
|
09b4de08e3 | ||
|
|
82b490c524 | ||
|
|
e0bc45f37e | ||
|
|
6e2197a254 | ||
|
|
de4ce73a9a | ||
|
|
f52c6f3b41 | ||
|
|
2271cc0044 | ||
|
|
931ffe4c84 | ||
|
|
45f39120da | ||
|
|
000e15640a | ||
|
|
d4ad11a63f | ||
|
|
c533966050 | ||
|
|
9b7d6b196f | ||
|
|
512a6cd4b9 | ||
|
|
6b0659af1f | ||
|
|
12279e9bda | ||
|
|
dadd501843 | ||
|
|
756f6310af | ||
|
|
7a19bedc25 | ||
|
|
a10a30a22c | ||
|
|
57e4ca355b | ||
|
|
fed3102492 | ||
|
|
64e0e256ce | ||
|
|
287581025e | ||
|
|
3184b9ea0d | ||
|
|
233eb7e785 | ||
|
|
a801f8f65f | ||
|
|
4afe654bcc | ||
|
|
926be245a9 | ||
|
|
f1615e6660 | ||
|
|
ac6ecf21a0 | ||
|
|
8629f0a26d | ||
|
|
7a6ef23adb | ||
|
|
dfa19c96b9 | ||
|
|
334c023b4f | ||
|
|
1f373e0b3e | ||
|
|
47fc4040d5 | ||
|
|
f5e47ab61a | ||
|
|
83cdb2ed2b | ||
|
|
d67eca8882 | ||
|
|
a0044dd3ad | ||
|
|
149ed8c266 | ||
|
|
65755a08aa | ||
|
|
1ea8e38372 | ||
|
|
69adb3fde2 | ||
|
|
28be9b5988 | ||
|
|
0590896453 | ||
|
|
8f59c4a236 | ||
|
|
550af91030 | ||
|
|
bb5721c473 | ||
|
|
68c93c65e6 | ||
|
|
d36efa5796 | ||
|
|
0a58f259ff | ||
|
|
649f0038bc | ||
|
|
9697e302d7 | ||
|
|
415d59eeb3 | ||
|
|
3cb06eb526 | ||
|
|
2087c775fc | ||
|
|
4dd4363e91 | ||
|
|
314050b568 | ||
|
|
1765a97f31 | ||
|
|
30ac8cb41c | ||
|
|
959b778cd7 | ||
|
|
ca87a5527d | ||
|
|
711c3d1b67 | ||
|
|
5210a68cc6 | ||
|
|
616e56e3f5 | ||
|
|
8e0075ff90 | ||
|
|
c980c70798 | ||
|
|
fa2763d8cd | ||
|
|
d18e472623 | ||
|
|
5f0c1ec70a | ||
|
|
4386999c38 | ||
|
|
9b07f3eb90 | ||
|
|
b280e18651 | ||
|
|
086bfa310f | ||
|
|
990ae8464e | ||
|
|
e4c82b5e8d | ||
|
|
4d790feaae | ||
|
|
b72dca0e2e | ||
|
|
e1a3a82f75 | ||
|
|
ec8700f3e9 | ||
|
|
d999bb3f09 | ||
|
|
a83d5132d9 | ||
|
|
1840e9a57a | ||
|
|
d876ba30d4 | ||
|
|
47bc073fb4 | ||
|
|
808e79e2ac | ||
|
|
371eacb354 | ||
|
|
f04f5921e1 | ||
|
|
035dd25ece | ||
|
|
03fa2f86d7 | ||
|
|
f999f4a467 | ||
|
|
5cfadaea7f | ||
|
|
562235f828 | ||
|
|
d6f6ea9dba | ||
|
|
8bc240b934 | ||
|
|
3ae4113043 | ||
|
|
1f52435bc1 | ||
|
|
4d0dfcca61 | ||
|
|
ea9076652a | ||
|
|
af1a464d87 | ||
|
|
50fe6041f0 | ||
|
|
555a4b6b05 | ||
|
|
7709be8118 | ||
|
|
130ac76e0c | ||
|
|
ae315f1789 | ||
|
|
4782e72d88 | ||
|
|
d69cafee01 | ||
|
|
e066ed58ca | ||
|
|
05a65cbf9d |
@@ -2,6 +2,12 @@
|
||||
"extends": ["airbnb", "next/core-web-vitals", "prettier"],
|
||||
"plugins": ["prettier"],
|
||||
"rules": {
|
||||
"import/no-cycle": [
|
||||
"error",
|
||||
{
|
||||
"maxDepth": 1
|
||||
}
|
||||
],
|
||||
"import/order": [
|
||||
"error",
|
||||
{
|
||||
|
||||
6
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"files.exclude": {
|
||||
"**/.next": true,
|
||||
"**/node_modules": true
|
||||
}
|
||||
}
|
||||
84
README.md
@@ -1,4 +1,25 @@
|
||||

|
||||
<p align="center">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="images/banner_light@2x.png">
|
||||
<img src="images/banner_dark@2x.png" width="65%">
|
||||
</picture>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
A modern <em>(fully static, fast)</em>, secure <em>(fully proxied)</em>, highly customizable application dashboard with integrations for more than 25 services and translations for over 15 languages. Easily configured via YAML files (or discovery via docker labels).
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="images/1.png" />
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="images/2.png" width="19%" />
|
||||
<img src="images/3.png" width="19%" />
|
||||
<img src="images/4.png" width="19%" />
|
||||
<img src="images/5.png" width="19%" />
|
||||
<img src="images/6.png" width="19%" />
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://discord.gg/k4ruYNrudu"><img src="https://img.shields.io/badge/Discord - Chat-blue?logo=discord&logoColor=white" /></a>
|
||||
@@ -6,18 +27,18 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/benphelps/homepage/actions/workflows/docker-publish.yml"><img src="https://github.com/benphelps/homepage/actions/workflows/docker-publish.yml/badge.svg" alt="Docker"></a>
|
||||
<a href="https://hosted.weblate.org/engage/homepage/"><img src="https://hosted.weblate.org/widgets/homepage/-/homepage/svg-badge.svg" alt="Weblate"></a>
|
||||
<a href="https://github.com/benphelps/homepage/actions/workflows/docker-publish.yml"><img src="https://github.com/benphelps/homepage/actions/workflows/docker-publish.yml/badge.svg" alt="Docker"></a>
|
||||
<a href="https://hosted.weblate.org/engage/homepage/"><img src="https://hosted.weblate.org/widgets/homepage/-/homepage/svg-badge.svg" alt="Weblate"></a>
|
||||
</p>
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- Fast! The entire site is statically generated at build time, so you can expect instant load times
|
||||
- **Fast!** The entire site is statically generated at build time, so you can expect instant load times
|
||||
- **Secure!** Every API request to backend services goes through a proxy server, so your API keys are never exposed to the frontend client.
|
||||
- Images built for AMD64 (x86_64), ARM64, ARMv7 and ARMv6
|
||||
- Supports all Raspberry Pi's, most SBCs & Apple Silicon
|
||||
- Full i18n support with automatic language detection
|
||||
- Translations for Chinese, Dutch, French, German, Hebrew, Hungarian, Norwegian Bokmål, Polish, Portuguese, Russian, Spanish and Swedish
|
||||
- Translantions for Catalan, Chinese, Dutch, Finnish, French, German, Hebrew, Hungarian, Norwegian Bokmål, Polish, Portuguese, Portuguese (Brazil), Romainian, Russian, Spanish, Swedish and Yue
|
||||
- Want to help translate? [Join the Weblate project](https://hosted.weblate.org/engage/homepage/)
|
||||
- Service & Web Bookmarks
|
||||
- Docker Integration
|
||||
@@ -26,17 +47,17 @@
|
||||
- Service Integration
|
||||
- Sonarr, Radarr, Readarr, Prowlarr, Bazarr, Lidarr, Emby, Jellyfin, Tautulli (Plex)
|
||||
- Ombi, Overseerr, Jellyseerr, Jackett, NZBGet, SABnzbd, ruTorrent, Transmission, qBittorrent
|
||||
- Portainer, Traefik, Speedtest Tracker, PiHole, AdGuard Home, Nginx Proxy Manager, Gotify, Syncthing Relay Server
|
||||
- Portainer, Traefik, Speedtest Tracker, PiHole, AdGuard Home, Nginx Proxy Manager, Gotify, Syncthing Relay Server, Authentic, Proxmox
|
||||
- Information Providers
|
||||
- Coin Market Cap, Mastodon
|
||||
- Information & Utility Widgets
|
||||
- System Stats (Disk, CPU, Memory)
|
||||
- Weather via WeatherAPI.com or OpenWeatherMap
|
||||
- Automatic location detection (with HTTPS), or manual location selection
|
||||
- Search Bar
|
||||
- Customizable
|
||||
- 21 theme colors with light and dark mode support
|
||||
- Background image support
|
||||
- Column and Row layout options
|
||||
|
||||
## Support & Suggestions
|
||||
|
||||
@@ -127,26 +148,43 @@ Huge thanks to the all the contributors who have helped make this project what i
|
||||
|
||||
- [aidenpwnz](https://github.com/benphelps/homepage/commits?author=aidenpwnz) - Nginx Proxy Manager, Search Bar Widget
|
||||
- [AlexFullmoon](https://github.com/benphelps/homepage/commits?author=AlexFullmoon) - OpenWeatherMap Widget
|
||||
- [AmadeusGraves](https://github.com/benphelps/homepage/commits?author=AmadeusGraves) - Spanish Translation
|
||||
- [andrii-kryvoviaz](https://github.com/benphelps/homepage/commits?author=andrii-kryvoviaz) - Background opacity option
|
||||
- [boerniee](https://github.com/benphelps/homepage/commits?author=boerniee) - German Translation
|
||||
- [comradekingu](https://github.com/benphelps/homepage/commits?author=comradekingu) - Norwegian Bokmål Translation
|
||||
- Daniel Varga - German & Hungarian Translation
|
||||
- [deffcolony](https://github.com/benphelps/homepage/commits?author=deffcolony) - Dutch Translation
|
||||
- [desolaris](https://github.com/benphelps/homepage/commits?author=desolaris) - Russian Translation
|
||||
- [DevPGSV](https://github.com/benphelps/homepage/commits?author=DevPGSV) - Syncthing Relay Server & Mastodon widgets
|
||||
- [ilusi0n](https://github.com/benphelps/homepage/commits?author=ilusi0n) - Jellyseerr Integration
|
||||
- [ItsJustMeChris](https://github.com/benphelps/homepage/commits?author=ItsJustMeChris) - Coin Market Cap Widget
|
||||
- [jackblk](https://github.com/benphelps/homepage/commits?author=jackblk) - Vietnamese Translation
|
||||
- [JazzFisch](https://github.com/benphelps/homepage/commits?author=JazzFisch) - Readarr, Bazarr, Lidarr, SABnzbd, Transmission & qBittorrent Integrations
|
||||
- [juanmanuelbc](https://github.com/benphelps/homepage/commits?author=juanmanuelbc) - Spanish and Catalan Translations
|
||||
- [JazzFisch](https://github.com/benphelps/homepage/commits?author=JazzFisch) - Readarr, Bazarr, Lidarr, SABnzbd, Transmission, qBittorrent, Proxmox Integrations & countless more improvements
|
||||
- [josways](https://github.com/benphelps/homepage/commits?author=josways) - Baidu search provider
|
||||
- [mauricio-kalil](https://github.com/benphelps/homepage/commits?author=mauricio-kalil) - Portuguese (Brazil)
|
||||
- [modem7](https://github.com/benphelps/homepage/commits?author=modem7) - Impvoed Docker Image
|
||||
- [nicedc](https://github.com/benphelps/homepage/commits?author=nicedc) - Chinese Translation
|
||||
- [Nonoss117](https://github.com/benphelps/homepage/commits?author=Nonoss117) - French Translation
|
||||
- [pacoculebras](https://github.com/benphelps/homepage/commits?author=pacoculebras) - Catalan Translation
|
||||
- [psychodracon](https://github.com/benphelps/homepage/commits?author=psychodracon) - Polish Translation
|
||||
- [MountainGod2](https://github.com/benphelps/homepage/discussions/243) - Homepage Logo
|
||||
- [quod](https://github.com/benphelps/homepage/commits?author=quod) - Fixed Typos
|
||||
- [schklom](https://github.com/benphelps/homepage/commits?author=schklom) - ARM64, ARMv7 and ARMv6
|
||||
- [ShlomiPorush](https://github.com/benphelps/homepage/commits?author=ShlomiPorush) - Hebrew Translation
|
||||
- [SuperDOS](https://github.com/benphelps/homepage/commits?author=SuperDOS) - Swedish Translation
|
||||
- [xicopitz](https://github.com/benphelps/homepage/commits?author=xicopitz) - Gotify & Prowlarr Integration
|
||||
|
||||
### Translators
|
||||
|
||||
- [3vilson](https://github.com/benphelps/homepage/commits?author=3vilson) - German
|
||||
- [4lenz1](https://github.com/benphelps/homepage/commits?author=4lenz1) - Chinese
|
||||
- [AmadeusGraves](https://github.com/benphelps/homepage/commits?author=AmadeusGraves) - Spanish
|
||||
- [boerniee](https://github.com/benphelps/homepage/commits?author=boerniee) - German
|
||||
- [brunoccr](https://github.com/benphelps/homepage/commits?author=brunoccr) - Portuguese (Brazil)
|
||||
- [C8opmBM](https://github.com/benphelps/homepage/commits?author=C8opmBM) - Romainian
|
||||
- [comradekingu](https://github.com/benphelps/homepage/commits?author=comradekingu) - Norwegian Bokmål
|
||||
- Daniel Varga - German & Hungarian
|
||||
- [deffcolony](https://github.com/benphelps/homepage/commits?author=deffcolony) - Dutch
|
||||
- [desolaris](https://github.com/benphelps/homepage/commits?author=desolaris) - Russian
|
||||
- [ericlokz](https://github.com/benphelps/homepage/commits?author=ericlokz) - Yue
|
||||
- [FunsKiTo](https://github.com/benphelps/homepage/commits?author=FunsKiTo) - Spanish
|
||||
- [jackblk](https://github.com/benphelps/homepage/commits?author=jackblk) - Vietnamese
|
||||
- [juanmanuelbc](https://github.com/benphelps/homepage/commits?author=juanmanuelbc) - Spanish and Catalan
|
||||
- [ling0412](https://github.com/benphelps/homepage/commits?author=ling0412) - Chinese
|
||||
- [milotype](https://github.com/benphelps/homepage/commits?author=milotype) - Croatian
|
||||
- [nicedc](https://github.com/benphelps/homepage/commits?author=nicedc) - Chinese
|
||||
- [Nonoss117](https://github.com/benphelps/homepage/commits?author=Nonoss117) - French
|
||||
- [pacoculebras](https://github.com/benphelps/homepage/commits?author=pacoculebras) - Catalan
|
||||
- [Prilann](https://github.com/benphelps/homepage/commits?author=Prilann) - German
|
||||
- [psychodracon](https://github.com/benphelps/homepage/commits?author=psychodracon) - Polish
|
||||
- Sascha Jelinek - German
|
||||
- [ShlomiPorush](https://github.com/benphelps/homepage/commits?author=ShlomiPorush) - Hebrew
|
||||
- [SuperDOS](https://github.com/benphelps/homepage/commits?author=SuperDOS) - Swedish
|
||||
- [kaihu](https://github.com/benphelps/homepage/commits?author=kaihu) - Finnish
|
||||
|
||||
BIN
images/1.png
Normal file
|
After Width: | Height: | Size: 345 KiB |
BIN
images/2.png
Normal file
|
After Width: | Height: | Size: 237 KiB |
BIN
images/3.png
Normal file
|
After Width: | Height: | Size: 279 KiB |
BIN
images/4.png
Normal file
|
After Width: | Height: | Size: 129 KiB |
BIN
images/5.png
Normal file
|
After Width: | Height: | Size: 423 KiB |
BIN
images/6.png
Normal file
|
After Width: | Height: | Size: 533 KiB |
BIN
images/banner_dark@2x.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
images/banner_light@2x.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 777 KiB |
124
next-i18next.config.js
Normal file
@@ -0,0 +1,124 @@
|
||||
// prettyBytes taken from https://github.com/sindresorhus/pretty-bytes
|
||||
|
||||
/* eslint-disable no-param-reassign */
|
||||
const BYTE_UNITS = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||
|
||||
const BIBYTE_UNITS = ["B", "kiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"];
|
||||
|
||||
const BIT_UNITS = ["b", "kbit", "Mbit", "Gbit", "Tbit", "Pbit", "Ebit", "Zbit", "Ybit"];
|
||||
|
||||
const BIBIT_UNITS = ["b", "kibit", "Mibit", "Gibit", "Tibit", "Pibit", "Eibit", "Zibit", "Yibit"];
|
||||
|
||||
/*
|
||||
Formats the given number using `Number#toLocaleString`.
|
||||
- If locale is a string, the value is expected to be a locale-key (for example: `de`).
|
||||
- If locale is true, the system default locale is used for translation.
|
||||
- If no value for locale is specified, the number is returned unmodified.
|
||||
*/
|
||||
const toLocaleString = (number, locale, options) => {
|
||||
let result = number;
|
||||
if (typeof locale === "string" || Array.isArray(locale)) {
|
||||
result = number.toLocaleString(locale, options);
|
||||
} else if (locale === true || options !== undefined) {
|
||||
result = number.toLocaleString(undefined, options);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
function prettyBytes(number, options) {
|
||||
if (!Number.isFinite(number)) {
|
||||
throw new TypeError(`Expected a finite number, got ${typeof number}: ${number}`);
|
||||
}
|
||||
|
||||
options = {
|
||||
bits: false,
|
||||
binary: false,
|
||||
...options,
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
const UNITS = options.bits ? (options.binary ? BIBIT_UNITS : BIT_UNITS) : options.binary ? BIBYTE_UNITS : BYTE_UNITS;
|
||||
|
||||
if (options.signed && number === 0) {
|
||||
return ` 0 ${UNITS[0]}`;
|
||||
}
|
||||
|
||||
const isNegative = number < 0;
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
const prefix = isNegative ? "-" : options.signed ? "+" : "";
|
||||
|
||||
if (isNegative) {
|
||||
number = -number;
|
||||
}
|
||||
|
||||
let localeOptions;
|
||||
|
||||
if (options.minimumFractionDigits !== undefined) {
|
||||
localeOptions = { minimumFractionDigits: options.minimumFractionDigits };
|
||||
}
|
||||
|
||||
if (options.maximumFractionDigits !== undefined) {
|
||||
localeOptions = { maximumFractionDigits: options.maximumFractionDigits, ...localeOptions };
|
||||
}
|
||||
|
||||
if (number < 1) {
|
||||
const numberString = toLocaleString(number, options.locale, localeOptions);
|
||||
return `${prefix + numberString} ${UNITS[0]}`;
|
||||
}
|
||||
|
||||
const exponent = Math.min(
|
||||
Math.floor(options.binary ? Math.log(number) / Math.log(1024) : Math.log10(number) / 3),
|
||||
UNITS.length - 1
|
||||
);
|
||||
number /= (options.binary ? 1024 : 1000) ** exponent;
|
||||
|
||||
if (!localeOptions) {
|
||||
number = number.toPrecision(3);
|
||||
}
|
||||
|
||||
const numberString = toLocaleString(Number(number), options.locale, localeOptions);
|
||||
|
||||
const unit = UNITS[exponent];
|
||||
|
||||
return `${prefix + numberString} ${unit}`;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
i18n: {
|
||||
defaultLocale: "en",
|
||||
locales: ["en"],
|
||||
},
|
||||
serializeConfig: false,
|
||||
use: [
|
||||
{
|
||||
init: (i18next) => {
|
||||
i18next.services.formatter.add("bytes", (value, lng, options) =>
|
||||
prettyBytes(parseFloat(value), { locale: lng, ...options })
|
||||
);
|
||||
|
||||
i18next.services.formatter.add("rate", (value, lng, options) => {
|
||||
if (value === 0) return "0 Bps";
|
||||
|
||||
const bits = options.bits ? value : value / 8;
|
||||
const k = 1024;
|
||||
const dm = options.decimals ? options.decimals : 0;
|
||||
const sizes = ["Bps", "Kbps", "Mbps", "Gbps", "Tbps", "Pbps", "Ebps", "Zbps", "Ybps"];
|
||||
|
||||
const i = Math.floor(Math.log(bits) / Math.log(k));
|
||||
|
||||
const formatted = new Intl.NumberFormat(lng, { maximumFractionDigits: dm, minimumFractionDigits: dm }).format(
|
||||
parseFloat(bits / k ** i)
|
||||
);
|
||||
|
||||
return `${formatted} ${sizes[i]}`;
|
||||
});
|
||||
|
||||
i18next.services.formatter.add("percent", (value, lng, options) =>
|
||||
new Intl.NumberFormat(lng, { style: "percent", ...options }).format(parseFloat(value) / 100.0)
|
||||
);
|
||||
},
|
||||
type: "3rdParty",
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,3 +1,5 @@
|
||||
const { i18n } = require("./next-i18next.config");
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
@@ -7,6 +9,7 @@ const nextConfig = {
|
||||
domains: ["cdn.jsdelivr.net"],
|
||||
unoptimized: true,
|
||||
},
|
||||
i18n,
|
||||
};
|
||||
|
||||
module.exports = nextConfig;
|
||||
|
||||
24
package.json
@@ -10,19 +10,17 @@
|
||||
"telemetry": "next telemetry disable"
|
||||
},
|
||||
"dependencies": {
|
||||
"@headlessui/react": "^1.7.0",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"classnames": "^2.3.1",
|
||||
"@headlessui/react": "^1.7.2",
|
||||
"classnames": "^2.3.2",
|
||||
"compare-versions": "^5.0.1",
|
||||
"dockerode": "^3.3.4",
|
||||
"follow-redirects": "^1.15.2",
|
||||
"i18next": "^21.9.1",
|
||||
"i18next-browser-languagedetector": "^6.1.5",
|
||||
"i18next-http-backend": "^1.4.1",
|
||||
"i18next": "^21.9.2",
|
||||
"js-yaml": "^4.1.0",
|
||||
"json-rpc-2.0": "^1.4.1",
|
||||
"memory-cache": "^0.2.0",
|
||||
"next": "^12.3.0",
|
||||
"next": "^12.3.1",
|
||||
"next-i18next": "^12.0.1",
|
||||
"node-os-utils": "^1.3.7",
|
||||
"pretty-bytes": "^6.0.0",
|
||||
"raw-body": "^2.5.1",
|
||||
@@ -33,15 +31,16 @@
|
||||
"rutorrent-promise": "^2.0.0",
|
||||
"shvl": "^3.0.0",
|
||||
"swr": "^1.3.0",
|
||||
"tailwind-scrollbar": "^2.0.1",
|
||||
"tough-cookie": "^4.1.2",
|
||||
"winston": "^3.8.2"
|
||||
"winston": "^3.8.2",
|
||||
"xml-js": "^1.6.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^10.4.9",
|
||||
"eslint": "^8.23.1",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"autoprefixer": "^10.4.12",
|
||||
"eslint": "^8.24.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-config-next": "^12.3.0",
|
||||
"eslint-config-next": "^12.3.1",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.6.1",
|
||||
@@ -50,6 +49,7 @@
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"postcss": "^8.4.16",
|
||||
"prettier": "^2.7.1",
|
||||
"tailwind-scrollbar": "^2.0.1",
|
||||
"tailwindcss": "^3.1.8",
|
||||
"typescript": "^4.8.3"
|
||||
}
|
||||
|
||||
588
pnpm-lock.yaml
generated
BIN
public/android-chrome-192x192.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
public/android-chrome-512x512.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
BIN
public/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
public/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 465 B |
BIN
public/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 595 B |
|
Before Width: | Height: | Size: 15 KiB |
BIN
public/homepage.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
205
public/locales/bg/common.json
Normal file
@@ -0,0 +1,205 @@
|
||||
{
|
||||
"sabnzbd": {
|
||||
"queue": "Опашка",
|
||||
"timeleft": "Оставащо Време",
|
||||
"rate": "Rate"
|
||||
},
|
||||
"rutorrent": {
|
||||
"active": "Акитивен",
|
||||
"upload": "Споделяне",
|
||||
"download": "Сваляне"
|
||||
},
|
||||
"widget": {
|
||||
"missing_type": "Липсваща приставка: {{type}}",
|
||||
"api_error": "API Грешка",
|
||||
"status": "Статус"
|
||||
},
|
||||
"weather": {
|
||||
"current": "Текущо местоположение",
|
||||
"allow": "Разреши",
|
||||
"updating": "Обновяване",
|
||||
"wait": "Моля изчакайте"
|
||||
},
|
||||
"search": {
|
||||
"placeholder": "Търсене…"
|
||||
},
|
||||
"resources": {
|
||||
"cpu": "CPU",
|
||||
"total": "Общо",
|
||||
"free": "Свободни",
|
||||
"used": "Заети",
|
||||
"load": "Натоварване"
|
||||
},
|
||||
"unifi": {
|
||||
"users": "Потребители",
|
||||
"uptime": "Активен от",
|
||||
"days": "Дни",
|
||||
"wan": "WAN",
|
||||
"lan_users": "LAN Потребители",
|
||||
"wlan_users": "WLAN Потребители",
|
||||
"up": "UP",
|
||||
"down": "DOWN",
|
||||
"wait": "Моля изчакайте"
|
||||
},
|
||||
"docker": {
|
||||
"offline": "Изключен",
|
||||
"rx": "RX",
|
||||
"tx": "TX",
|
||||
"mem": "MEM",
|
||||
"cpu": "CPU"
|
||||
},
|
||||
"emby": {
|
||||
"playing": "Възпроизвежда",
|
||||
"transcoding": "Конвертира",
|
||||
"bitrate": "Bitrate",
|
||||
"no_active": "Няма активни потоци"
|
||||
},
|
||||
"tautulli": {
|
||||
"playing": "Възпроизвежда",
|
||||
"transcoding": "Конвертира",
|
||||
"bitrate": "Честота",
|
||||
"no_active": "Няма активни потоци"
|
||||
},
|
||||
"nzbget": {
|
||||
"rate": "Rate",
|
||||
"remaining": "Остава",
|
||||
"downloaded": "Изтеглени"
|
||||
},
|
||||
"plex": {
|
||||
"streams": "Активни Потоци",
|
||||
"movies": "Филми",
|
||||
"tv": "Сериали"
|
||||
},
|
||||
"transmission": {
|
||||
"download": "Сваляне",
|
||||
"upload": "Upload",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"qbittorrent": {
|
||||
"download": "Download",
|
||||
"upload": "Upload",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"sonarr": {
|
||||
"wanted": "Wanted",
|
||||
"queued": "Queued",
|
||||
"series": "Series"
|
||||
},
|
||||
"radarr": {
|
||||
"wanted": "Wanted",
|
||||
"queued": "Queued",
|
||||
"movies": "Movies"
|
||||
},
|
||||
"lidarr": {
|
||||
"wanted": "Wanted",
|
||||
"queued": "Queued",
|
||||
"albums": "Albums"
|
||||
},
|
||||
"readarr": {
|
||||
"wanted": "Wanted",
|
||||
"queued": "Queued",
|
||||
"books": "Books"
|
||||
},
|
||||
"bazarr": {
|
||||
"missingEpisodes": "Missing Episodes",
|
||||
"missingMovies": "Missing Movies"
|
||||
},
|
||||
"ombi": {
|
||||
"pending": "Pending",
|
||||
"approved": "Approved",
|
||||
"available": "Available"
|
||||
},
|
||||
"jellyseerr": {
|
||||
"pending": "Pending",
|
||||
"approved": "Approved",
|
||||
"available": "Available"
|
||||
},
|
||||
"overseerr": {
|
||||
"pending": "Pending",
|
||||
"approved": "Approved",
|
||||
"available": "Available"
|
||||
},
|
||||
"pihole": {
|
||||
"queries": "Queries",
|
||||
"blocked": "Blocked",
|
||||
"gravity": "Gravity"
|
||||
},
|
||||
"adguard": {
|
||||
"queries": "Queries",
|
||||
"blocked": "Blocked",
|
||||
"filtered": "Filtered",
|
||||
"latency": "Latency"
|
||||
},
|
||||
"speedtest": {
|
||||
"upload": "Upload",
|
||||
"download": "Download",
|
||||
"ping": "Ping"
|
||||
},
|
||||
"portainer": {
|
||||
"running": "Running",
|
||||
"stopped": "Stopped",
|
||||
"total": "Total"
|
||||
},
|
||||
"traefik": {
|
||||
"routers": "Routers",
|
||||
"services": "Services",
|
||||
"middleware": "Middleware"
|
||||
},
|
||||
"npm": {
|
||||
"enabled": "Enabled",
|
||||
"disabled": "Disabled",
|
||||
"total": "Total"
|
||||
},
|
||||
"coinmarketcap": {
|
||||
"configure": "Configure one or more crypto currencies to track",
|
||||
"1hour": "1 Hour",
|
||||
"1day": "1 Day",
|
||||
"7days": "7 Days",
|
||||
"30days": "30 Days"
|
||||
},
|
||||
"gotify": {
|
||||
"apps": "Applications",
|
||||
"clients": "Clients",
|
||||
"messages": "Messages"
|
||||
},
|
||||
"prowlarr": {
|
||||
"enableIndexers": "Indexers",
|
||||
"numberOfGrabs": "Grabs",
|
||||
"numberOfQueries": "Queries",
|
||||
"numberOfFailGrabs": "Fail Grabs",
|
||||
"numberOfFailQueries": "Fail Queries"
|
||||
},
|
||||
"authentik": {
|
||||
"loginsLast24H": "Logins (24h)",
|
||||
"users": "Users",
|
||||
"failedLoginsLast24H": "Failed Logins (24h)"
|
||||
},
|
||||
"jackett": {
|
||||
"configured": "Configured",
|
||||
"errored": "Errored"
|
||||
},
|
||||
"strelaysrv": {
|
||||
"numActiveSessions": "Sessions",
|
||||
"numConnections": "Connections",
|
||||
"dataRelayed": "Relayed",
|
||||
"transferRate": "Rate"
|
||||
},
|
||||
"mastodon": {
|
||||
"user_count": "Users",
|
||||
"status_count": "Posts",
|
||||
"domain_count": "Domains"
|
||||
},
|
||||
"proxmox": {
|
||||
"mem": "MEM",
|
||||
"cpu": "CPU",
|
||||
"lxc": "LXC",
|
||||
"vms": "VMs"
|
||||
},
|
||||
"glances": {
|
||||
"cpu": "CPU",
|
||||
"mem": "MEM",
|
||||
"wait": "Please wait"
|
||||
}
|
||||
}
|
||||
@@ -33,13 +33,14 @@
|
||||
"total": "Total",
|
||||
"free": "Lliure",
|
||||
"used": "Usat",
|
||||
"load": "Càrrega"
|
||||
"load": "Càrrega",
|
||||
"cpu": "CPU"
|
||||
},
|
||||
"docker": {
|
||||
"rx": "RX",
|
||||
"tx": "TX",
|
||||
"mem": "MEM",
|
||||
"cpu": "CPU",
|
||||
"rx": "Rebut",
|
||||
"tx": "Transmès",
|
||||
"mem": "Memòria",
|
||||
"cpu": "Processador",
|
||||
"offline": "Fora de línia"
|
||||
},
|
||||
"emby": {
|
||||
@@ -168,5 +169,37 @@
|
||||
"numConnections": "Connexions",
|
||||
"dataRelayed": "Transmès",
|
||||
"transferRate": "Velocitat"
|
||||
},
|
||||
"authentik": {
|
||||
"users": "Usuaris",
|
||||
"loginsLast24H": "Inicis de sessió (24h)",
|
||||
"failedLoginsLast24H": "Errors d'inici de sessió (24h)"
|
||||
},
|
||||
"proxmox": {
|
||||
"vms": "VMs",
|
||||
"mem": "Memòria",
|
||||
"cpu": "Processador",
|
||||
"lxc": "LXC"
|
||||
},
|
||||
"unifi": {
|
||||
"users": "Users",
|
||||
"uptime": "System Uptime",
|
||||
"days": "Days",
|
||||
"wan": "WAN",
|
||||
"lan_users": "LAN Users",
|
||||
"wlan_users": "WLAN Users",
|
||||
"up": "UP",
|
||||
"down": "DOWN",
|
||||
"wait": "Please wait"
|
||||
},
|
||||
"plex": {
|
||||
"streams": "Active Streams",
|
||||
"movies": "Movies",
|
||||
"tv": "TV Shows"
|
||||
},
|
||||
"glances": {
|
||||
"cpu": "CPU",
|
||||
"mem": "MEM",
|
||||
"wait": "Please wait"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
"total": "Gesamt",
|
||||
"free": "Frei",
|
||||
"used": "Gebraucht",
|
||||
"load": "Belastung"
|
||||
"load": "Belastung",
|
||||
"cpu": "CPU"
|
||||
},
|
||||
"docker": {
|
||||
"rx": "Rx",
|
||||
@@ -168,5 +169,37 @@
|
||||
"numConnections": "Verbindungen",
|
||||
"dataRelayed": "Weitergeleitet",
|
||||
"transferRate": "Bewerten"
|
||||
},
|
||||
"authentik": {
|
||||
"users": "Benutzer",
|
||||
"loginsLast24H": "Anmeldungen (24h)",
|
||||
"failedLoginsLast24H": "fehlerhafte Anmeldungen (24h)"
|
||||
},
|
||||
"proxmox": {
|
||||
"mem": "MEM",
|
||||
"cpu": "CPU",
|
||||
"lxc": "LXC",
|
||||
"vms": "VMs"
|
||||
},
|
||||
"unifi": {
|
||||
"users": "Users",
|
||||
"uptime": "System Uptime",
|
||||
"days": "Days",
|
||||
"wan": "WAN",
|
||||
"lan_users": "LAN Users",
|
||||
"wlan_users": "WLAN Users",
|
||||
"up": "UP",
|
||||
"down": "DOWN",
|
||||
"wait": "Please wait"
|
||||
},
|
||||
"plex": {
|
||||
"streams": "Active Streams",
|
||||
"movies": "Movies",
|
||||
"tv": "TV Shows"
|
||||
},
|
||||
"glances": {
|
||||
"cpu": "CPU",
|
||||
"mem": "MEM",
|
||||
"wait": "Please wait"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,11 +25,23 @@
|
||||
"placeholder": "Search…"
|
||||
},
|
||||
"resources": {
|
||||
"cpu": "CPU",
|
||||
"total": "Total",
|
||||
"free": "Free",
|
||||
"used": "Used",
|
||||
"load": "Load"
|
||||
},
|
||||
"unifi": {
|
||||
"users": "Users",
|
||||
"uptime": "System Uptime",
|
||||
"days": "Days",
|
||||
"wan": "WAN",
|
||||
"lan_users": "LAN Users",
|
||||
"wlan_users": "WLAN Users",
|
||||
"up": "UP",
|
||||
"down": "DOWN",
|
||||
"wait": "Please wait"
|
||||
},
|
||||
"docker": {
|
||||
"rx": "RX",
|
||||
"tx": "TX",
|
||||
@@ -54,6 +66,11 @@
|
||||
"remaining": "Remaining",
|
||||
"downloaded": "Downloaded"
|
||||
},
|
||||
"plex": {
|
||||
"streams": "Active Streams",
|
||||
"movies": "Movies",
|
||||
"tv": "TV Shows"
|
||||
},
|
||||
"sabnzbd": {
|
||||
"rate": "Rate",
|
||||
"queue": "Queue",
|
||||
@@ -179,5 +196,21 @@
|
||||
"user_count": "Users",
|
||||
"status_count": "Posts",
|
||||
"domain_count": "Domains"
|
||||
},
|
||||
"authentik": {
|
||||
"users": "Users",
|
||||
"loginsLast24H": "Logins (24h)",
|
||||
"failedLoginsLast24H": "Failed Logins (24h)"
|
||||
},
|
||||
"proxmox": {
|
||||
"mem": "MEM",
|
||||
"cpu": "CPU",
|
||||
"lxc": "LXC",
|
||||
"vms": "VMs"
|
||||
},
|
||||
"glances": {
|
||||
"cpu": "CPU",
|
||||
"mem": "MEM",
|
||||
"wait": "Please wait"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
"total": "Total",
|
||||
"free": "Libre",
|
||||
"used": "Usado",
|
||||
"load": "Carga"
|
||||
"load": "Carga",
|
||||
"cpu": "CPU"
|
||||
},
|
||||
"docker": {
|
||||
"rx": "Recibido",
|
||||
@@ -38,17 +39,17 @@
|
||||
"download": "Bajada"
|
||||
},
|
||||
"sonarr": {
|
||||
"wanted": "Más deseado",
|
||||
"wanted": "Buscando",
|
||||
"queued": "En cola",
|
||||
"series": "Series"
|
||||
},
|
||||
"radarr": {
|
||||
"wanted": "Más deseado",
|
||||
"wanted": "Buscando",
|
||||
"queued": "En cola",
|
||||
"movies": "Películas"
|
||||
},
|
||||
"readarr": {
|
||||
"wanted": "Más deseado",
|
||||
"wanted": "Buscando",
|
||||
"queued": "En cola",
|
||||
"books": "Libros"
|
||||
},
|
||||
@@ -143,7 +144,7 @@
|
||||
},
|
||||
"lidarr": {
|
||||
"queued": "En cola",
|
||||
"wanted": "Más deseado",
|
||||
"wanted": "Buscando",
|
||||
"albums": "Álbumes"
|
||||
},
|
||||
"adguard": {
|
||||
@@ -168,5 +169,37 @@
|
||||
"numConnections": "Conexiones",
|
||||
"dataRelayed": "Retransmitido",
|
||||
"transferRate": "Velocidad"
|
||||
},
|
||||
"authentik": {
|
||||
"users": "Usuarios",
|
||||
"loginsLast24H": "Inicios de sesión (24h)",
|
||||
"failedLoginsLast24H": "Inicios de sesión fallidos (24h)"
|
||||
},
|
||||
"proxmox": {
|
||||
"mem": "Memoria",
|
||||
"cpu": "Procesador",
|
||||
"lxc": "LXC",
|
||||
"vms": "VMs"
|
||||
},
|
||||
"unifi": {
|
||||
"up": "UP",
|
||||
"users": "Users",
|
||||
"uptime": "System Uptime",
|
||||
"days": "Days",
|
||||
"wan": "WAN",
|
||||
"lan_users": "LAN Users",
|
||||
"wlan_users": "WLAN Users",
|
||||
"down": "DOWN",
|
||||
"wait": "Please wait"
|
||||
},
|
||||
"plex": {
|
||||
"streams": "Active Streams",
|
||||
"movies": "Movies",
|
||||
"tv": "TV Shows"
|
||||
},
|
||||
"glances": {
|
||||
"cpu": "CPU",
|
||||
"mem": "MEM",
|
||||
"wait": "Please wait"
|
||||
}
|
||||
}
|
||||
|
||||
205
public/locales/fi/common.json
Normal file
@@ -0,0 +1,205 @@
|
||||
{
|
||||
"widget": {
|
||||
"missing_type": "Puuttuva härpäkkeen tyyppi: {{type}}",
|
||||
"api_error": "API-virhe",
|
||||
"status": "Tila"
|
||||
},
|
||||
"weather": {
|
||||
"current": "Nykyinen sijainti",
|
||||
"allow": "Klikkaa salliaksesi",
|
||||
"updating": "Päivitetään",
|
||||
"wait": "Odota, ole hyvä"
|
||||
},
|
||||
"search": {
|
||||
"placeholder": "Hae…"
|
||||
},
|
||||
"resources": {
|
||||
"cpu": "CPU",
|
||||
"total": "Yhteensä",
|
||||
"free": "Vapaana",
|
||||
"used": "Käytetty",
|
||||
"load": "Kuorma"
|
||||
},
|
||||
"docker": {
|
||||
"rx": "RX",
|
||||
"tx": "TX",
|
||||
"mem": "RAM",
|
||||
"cpu": "CPU",
|
||||
"offline": "Offline"
|
||||
},
|
||||
"emby": {
|
||||
"playing": "Toistaa",
|
||||
"transcoding": "Transkoodaa",
|
||||
"bitrate": "Bittinopeus",
|
||||
"no_active": "Ei aktiivisia striimejä"
|
||||
},
|
||||
"tautulli": {
|
||||
"playing": "Toistaa",
|
||||
"transcoding": "Transkoodaa",
|
||||
"bitrate": "Bittinopeus",
|
||||
"no_active": "Ei aktiivisia striimejä"
|
||||
},
|
||||
"nzbget": {
|
||||
"rate": "Nopeus",
|
||||
"remaining": "Jäljellä",
|
||||
"downloaded": "Ladattu"
|
||||
},
|
||||
"sabnzbd": {
|
||||
"rate": "Nopeus",
|
||||
"queue": "Jono",
|
||||
"timeleft": "Aikaa jäljellä"
|
||||
},
|
||||
"rutorrent": {
|
||||
"active": "Aktiivinen",
|
||||
"upload": "Lähetys",
|
||||
"download": "Lataus"
|
||||
},
|
||||
"transmission": {
|
||||
"download": "Lataus",
|
||||
"upload": "Lähetys",
|
||||
"leech": "Lataajia",
|
||||
"seed": "Lähettäjiä"
|
||||
},
|
||||
"qbittorrent": {
|
||||
"download": "Lataus",
|
||||
"upload": "Lähetys",
|
||||
"leech": "Lataajia",
|
||||
"seed": "Lähettäjiä"
|
||||
},
|
||||
"sonarr": {
|
||||
"wanted": "Haluttu",
|
||||
"queued": "Jonossa",
|
||||
"series": "Sarja"
|
||||
},
|
||||
"radarr": {
|
||||
"wanted": "Haluttu",
|
||||
"queued": "Jonossa",
|
||||
"movies": "Elokuvia"
|
||||
},
|
||||
"lidarr": {
|
||||
"wanted": "Haluttu",
|
||||
"queued": "Jonossa",
|
||||
"albums": "Albumeja"
|
||||
},
|
||||
"readarr": {
|
||||
"wanted": "Haluttu",
|
||||
"queued": "Jonossa",
|
||||
"books": "Kirjoja"
|
||||
},
|
||||
"bazarr": {
|
||||
"missingEpisodes": "Puuttuvia jaksoja",
|
||||
"missingMovies": "Puuttuvia elokuvia"
|
||||
},
|
||||
"ombi": {
|
||||
"pending": "Vireillä",
|
||||
"approved": "Hyväksytty",
|
||||
"available": "Saatavilla"
|
||||
},
|
||||
"jellyseerr": {
|
||||
"pending": "Vireillä",
|
||||
"approved": "Hyväksytty",
|
||||
"available": "Saatavilla"
|
||||
},
|
||||
"overseerr": {
|
||||
"pending": "Vireillä",
|
||||
"approved": "Hyväksytty",
|
||||
"available": "Saatavilla"
|
||||
},
|
||||
"pihole": {
|
||||
"queries": "Kyselyjä",
|
||||
"blocked": "Estetty",
|
||||
"gravity": "Vakavuus"
|
||||
},
|
||||
"adguard": {
|
||||
"queries": "Kyselyjä",
|
||||
"blocked": "Estetty",
|
||||
"filtered": "Suodatettu",
|
||||
"latency": "Viive"
|
||||
},
|
||||
"speedtest": {
|
||||
"upload": "Lähetys",
|
||||
"download": "Lataus",
|
||||
"ping": "Viive"
|
||||
},
|
||||
"portainer": {
|
||||
"running": "Käynnissä",
|
||||
"stopped": "Pysäytetty",
|
||||
"total": "Yhteensä"
|
||||
},
|
||||
"traefik": {
|
||||
"routers": "Reitittimiä",
|
||||
"services": "Palveluja",
|
||||
"middleware": "Middlewareja"
|
||||
},
|
||||
"npm": {
|
||||
"enabled": "Käytössä",
|
||||
"disabled": "Poissa käytöstä",
|
||||
"total": "Yhteensä"
|
||||
},
|
||||
"coinmarketcap": {
|
||||
"configure": "Määritä yksi tai useampi kryptovaluutta seurattavaksi",
|
||||
"1hour": "1 tunti",
|
||||
"1day": "1 päivä",
|
||||
"7days": "7 päivää",
|
||||
"30days": "30 päivää"
|
||||
},
|
||||
"gotify": {
|
||||
"apps": "Sovelluksia",
|
||||
"clients": "Asiakasohjelmia",
|
||||
"messages": "Viestejä"
|
||||
},
|
||||
"prowlarr": {
|
||||
"enableIndexers": "Indeksoijia",
|
||||
"numberOfGrabs": "Nappauksia",
|
||||
"numberOfQueries": "Hakuja",
|
||||
"numberOfFailGrabs": "Epäonnistuneita nappauksia",
|
||||
"numberOfFailQueries": "Epäonnistuneita kyselyjä"
|
||||
},
|
||||
"jackett": {
|
||||
"configured": "Määritettyjä",
|
||||
"errored": "Virheellisiä"
|
||||
},
|
||||
"strelaysrv": {
|
||||
"numActiveSessions": "Istuntoja",
|
||||
"numConnections": "Yhteyksiä",
|
||||
"dataRelayed": "Välitetty",
|
||||
"transferRate": "Nopeus"
|
||||
},
|
||||
"mastodon": {
|
||||
"user_count": "Käyttäjiä",
|
||||
"status_count": "Kirjoituksia",
|
||||
"domain_count": "Verkkotunnuksia"
|
||||
},
|
||||
"authentik": {
|
||||
"users": "Käyttäjiä",
|
||||
"loginsLast24H": "Kirjautumisia (24h)",
|
||||
"failedLoginsLast24H": "Epäonnistuneita kirjautumisia (24h)"
|
||||
},
|
||||
"proxmox": {
|
||||
"mem": "RAM",
|
||||
"cpu": "CPU",
|
||||
"lxc": "LXC",
|
||||
"vms": "VKt"
|
||||
},
|
||||
"unifi": {
|
||||
"users": "Users",
|
||||
"uptime": "System Uptime",
|
||||
"lan_users": "LAN Users",
|
||||
"wlan_users": "WLAN Users",
|
||||
"wait": "Please wait",
|
||||
"days": "Days",
|
||||
"wan": "WAN",
|
||||
"up": "UP",
|
||||
"down": "DOWN"
|
||||
},
|
||||
"plex": {
|
||||
"streams": "Active Streams",
|
||||
"movies": "Movies",
|
||||
"tv": "TV Shows"
|
||||
},
|
||||
"glances": {
|
||||
"wait": "Please wait",
|
||||
"cpu": "CPU",
|
||||
"mem": "MEM"
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,8 @@
|
||||
"total": "Total",
|
||||
"free": "Libre",
|
||||
"used": "Utilisé",
|
||||
"load": "Charge"
|
||||
"load": "Charge",
|
||||
"cpu": "CPU"
|
||||
},
|
||||
"docker": {
|
||||
"rx": "Rx",
|
||||
@@ -87,17 +88,6 @@
|
||||
"disabled": "Désactivé",
|
||||
"total": "Total"
|
||||
},
|
||||
"common": {
|
||||
"bbytes": "{{value, bytes(binary: true)}}",
|
||||
"bytes": "{{value, bytes}}",
|
||||
"bits": "{{value, bytes(bits: true)}}",
|
||||
"bbits": "{{value, bytes(bits: true, binary: true)}}",
|
||||
"number": "{{value, number}}",
|
||||
"byterate": "{{value, bytes}}",
|
||||
"bitrate": "{{value, bytes(bits: true)}}",
|
||||
"percent": "{{value, percent}}",
|
||||
"ms": "{{value, number}}"
|
||||
},
|
||||
"weather": {
|
||||
"current": "Localisation actuelle",
|
||||
"allow": "Cliquez pour autoriser",
|
||||
@@ -179,5 +169,37 @@
|
||||
"numConnections": "Cnx",
|
||||
"dataRelayed": "Relayé",
|
||||
"transferRate": "Débit"
|
||||
},
|
||||
"authentik": {
|
||||
"users": "Utilisateurs",
|
||||
"loginsLast24H": "Cnx. (24h)",
|
||||
"failedLoginsLast24H": "Cnx. échouées (24h)"
|
||||
},
|
||||
"proxmox": {
|
||||
"mem": "Mém",
|
||||
"cpu": "Cpu",
|
||||
"lxc": "LxC",
|
||||
"vms": "VMs"
|
||||
},
|
||||
"unifi": {
|
||||
"users": "Utilisateurs",
|
||||
"uptime": "Disponibilité du système",
|
||||
"days": "Jours",
|
||||
"wan": "WAN",
|
||||
"lan_users": "Utilisateurs LAN",
|
||||
"wlan_users": "Utilisateurs WLAN",
|
||||
"up": "UP",
|
||||
"down": "DOWN",
|
||||
"wait": "Merci de patienter"
|
||||
},
|
||||
"plex": {
|
||||
"streams": "Flux actif",
|
||||
"movies": "Films",
|
||||
"tv": "Séries TV"
|
||||
},
|
||||
"glances": {
|
||||
"cpu": "CPU",
|
||||
"mem": "MEM",
|
||||
"wait": "Please wait"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
"total": "סה\"כ",
|
||||
"free": "פנוי",
|
||||
"used": "בשימוש",
|
||||
"load": "עומס"
|
||||
"load": "עומס",
|
||||
"cpu": "CPU"
|
||||
},
|
||||
"docker": {
|
||||
"rx": "RX",
|
||||
@@ -168,5 +169,37 @@
|
||||
"numConnections": "Connections",
|
||||
"dataRelayed": "Relayed",
|
||||
"transferRate": "Rate"
|
||||
},
|
||||
"authentik": {
|
||||
"users": "Users",
|
||||
"loginsLast24H": "Logins (24h)",
|
||||
"failedLoginsLast24H": "Failed Logins (24h)"
|
||||
},
|
||||
"proxmox": {
|
||||
"mem": "MEM",
|
||||
"cpu": "CPU",
|
||||
"lxc": "LXC",
|
||||
"vms": "VMs"
|
||||
},
|
||||
"unifi": {
|
||||
"users": "Users",
|
||||
"uptime": "System Uptime",
|
||||
"days": "Days",
|
||||
"wan": "WAN",
|
||||
"lan_users": "LAN Users",
|
||||
"wlan_users": "WLAN Users",
|
||||
"up": "UP",
|
||||
"down": "DOWN",
|
||||
"wait": "Please wait"
|
||||
},
|
||||
"plex": {
|
||||
"streams": "Active Streams",
|
||||
"movies": "Movies",
|
||||
"tv": "TV Shows"
|
||||
},
|
||||
"glances": {
|
||||
"cpu": "CPU",
|
||||
"mem": "MEM",
|
||||
"wait": "Please wait"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,172 +1,205 @@
|
||||
{
|
||||
"weather": {
|
||||
"current": "Current Location",
|
||||
"allow": "Click to allow",
|
||||
"updating": "Updating",
|
||||
"wait": "Please wait"
|
||||
"current": "Tranutačna lokacija",
|
||||
"allow": "Pritisni za dozvoljavanje",
|
||||
"updating": "Aktualiziranje",
|
||||
"wait": "Pričekaj"
|
||||
},
|
||||
"search": {
|
||||
"placeholder": "Search…"
|
||||
"placeholder": "Traži …"
|
||||
},
|
||||
"resources": {
|
||||
"total": "Total",
|
||||
"free": "Free",
|
||||
"used": "Used",
|
||||
"load": "Load"
|
||||
"total": "Ukupno",
|
||||
"free": "Slobodno",
|
||||
"used": "Korišteno",
|
||||
"load": "Opterećenje",
|
||||
"cpu": "CPU"
|
||||
},
|
||||
"sabnzbd": {
|
||||
"rate": "Rate",
|
||||
"queue": "Queue",
|
||||
"timeleft": "Time Left"
|
||||
"rate": "Stopa",
|
||||
"queue": "Red",
|
||||
"timeleft": "Preostalo vrijeme"
|
||||
},
|
||||
"overseerr": {
|
||||
"available": "Available",
|
||||
"pending": "Pending",
|
||||
"approved": "Approved"
|
||||
"available": "Dostupno",
|
||||
"pending": "Predstoji",
|
||||
"approved": "Odobreno"
|
||||
},
|
||||
"pihole": {
|
||||
"queries": "Queries",
|
||||
"blocked": "Blocked",
|
||||
"gravity": "Gravity"
|
||||
"queries": "Upiti",
|
||||
"blocked": "Blokirano",
|
||||
"gravity": "Ozbiljnost"
|
||||
},
|
||||
"adguard": {
|
||||
"latency": "Latency",
|
||||
"queries": "Queries",
|
||||
"blocked": "Blocked",
|
||||
"filtered": "Filtered"
|
||||
"latency": "Kašnjenje",
|
||||
"queries": "Upiti",
|
||||
"blocked": "Blokirano",
|
||||
"filtered": "Filtrirano"
|
||||
},
|
||||
"npm": {
|
||||
"total": "Total",
|
||||
"enabled": "Enabled",
|
||||
"disabled": "Disabled"
|
||||
"total": "Ukupno",
|
||||
"enabled": "Aktivirano",
|
||||
"disabled": "Deaktivirano"
|
||||
},
|
||||
"coinmarketcap": {
|
||||
"configure": "Configure one or more crypto currencies to track",
|
||||
"1hour": "1 Hour",
|
||||
"1day": "1 Day",
|
||||
"7days": "7 Days",
|
||||
"30days": "30 Days"
|
||||
"configure": "Konfiguriraj jednu ili više kripto valuta za praćenje",
|
||||
"1hour": "1 sat",
|
||||
"1day": "1 dan",
|
||||
"7days": "7 dana",
|
||||
"30days": "30 dana"
|
||||
},
|
||||
"prowlarr": {
|
||||
"enableIndexers": "Indexers",
|
||||
"numberOfGrabs": "Grabs",
|
||||
"numberOfQueries": "Queries",
|
||||
"numberOfFailGrabs": "Fail Grabs",
|
||||
"numberOfFailQueries": "Fail Queries"
|
||||
"enableIndexers": "Indeksatori",
|
||||
"numberOfGrabs": "Dohvaćanja",
|
||||
"numberOfQueries": "Upiti",
|
||||
"numberOfFailGrabs": "Neuspjela dohvaćanja",
|
||||
"numberOfFailQueries": "Neuspjeli upiti"
|
||||
},
|
||||
"widget": {
|
||||
"missing_type": "Missing Widget Type: {{type}}",
|
||||
"api_error": "API Error",
|
||||
"status": "Status"
|
||||
"missing_type": "Nedostajuća vrsta widgeta: {{type}}",
|
||||
"api_error": "API greška",
|
||||
"status": "Stanje"
|
||||
},
|
||||
"docker": {
|
||||
"rx": "RX",
|
||||
"tx": "TX",
|
||||
"mem": "MEM",
|
||||
"cpu": "CPU",
|
||||
"offline": "Offline"
|
||||
"offline": "Odspojen"
|
||||
},
|
||||
"emby": {
|
||||
"playing": "Playing",
|
||||
"transcoding": "Transcoding",
|
||||
"bitrate": "Bitrate",
|
||||
"no_active": "No Active Streams"
|
||||
"playing": "Reprodukcija",
|
||||
"transcoding": "Prekodiranje",
|
||||
"bitrate": "Brzina prijenosa",
|
||||
"no_active": "Nema aktivnih prijenosa"
|
||||
},
|
||||
"tautulli": {
|
||||
"playing": "Playing",
|
||||
"transcoding": "Transcoding",
|
||||
"bitrate": "Bitrate",
|
||||
"no_active": "No Active Streams"
|
||||
"playing": "Reprodukcija",
|
||||
"transcoding": "Prekodiranje",
|
||||
"bitrate": "Brzina prijenosa",
|
||||
"no_active": "Nema aktivnih prijenosa"
|
||||
},
|
||||
"nzbget": {
|
||||
"rate": "Rate",
|
||||
"remaining": "Remaining",
|
||||
"downloaded": "Downloaded"
|
||||
"rate": "Stopa",
|
||||
"remaining": "Preostalo",
|
||||
"downloaded": "Preuzeto"
|
||||
},
|
||||
"rutorrent": {
|
||||
"upload": "Upload",
|
||||
"download": "Download",
|
||||
"active": "Active"
|
||||
"upload": "Prijenos",
|
||||
"download": "Preuzimanje",
|
||||
"active": "Aktivno"
|
||||
},
|
||||
"transmission": {
|
||||
"download": "Download",
|
||||
"upload": "Upload",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
"download": "Preuzimanje",
|
||||
"upload": "Prijenos",
|
||||
"leech": "Krvopija",
|
||||
"seed": "Prijenos preuzetog sadržaja"
|
||||
},
|
||||
"sonarr": {
|
||||
"wanted": "Wanted",
|
||||
"queued": "Queued",
|
||||
"series": "Series"
|
||||
"wanted": "Željeno",
|
||||
"queued": "U redu čekanja",
|
||||
"series": "Serije"
|
||||
},
|
||||
"radarr": {
|
||||
"wanted": "Wanted",
|
||||
"queued": "Queued",
|
||||
"movies": "Movies"
|
||||
"wanted": "Željeno",
|
||||
"queued": "U redu čekanja",
|
||||
"movies": "Filmovi"
|
||||
},
|
||||
"lidarr": {
|
||||
"wanted": "Wanted",
|
||||
"queued": "Queued",
|
||||
"albums": "Albums"
|
||||
"wanted": "Željeno",
|
||||
"queued": "U redu čekanja",
|
||||
"albums": "Albumi"
|
||||
},
|
||||
"readarr": {
|
||||
"wanted": "Wanted",
|
||||
"queued": "Queued",
|
||||
"books": "Books"
|
||||
"wanted": "Željeno",
|
||||
"queued": "U redu čekanja",
|
||||
"books": "Knjige"
|
||||
},
|
||||
"bazarr": {
|
||||
"missingEpisodes": "Missing Episodes",
|
||||
"missingMovies": "Missing Movies"
|
||||
"missingEpisodes": "Nedostajuće epizode",
|
||||
"missingMovies": "Nedostajući filmovi"
|
||||
},
|
||||
"ombi": {
|
||||
"pending": "Pending",
|
||||
"approved": "Approved",
|
||||
"available": "Available"
|
||||
"pending": "Predstoji",
|
||||
"approved": "Odobreno",
|
||||
"available": "Dostupno"
|
||||
},
|
||||
"jellyseerr": {
|
||||
"pending": "Pending",
|
||||
"approved": "Approved",
|
||||
"available": "Available"
|
||||
"pending": "Predstoji",
|
||||
"approved": "Odobreno",
|
||||
"available": "Dostupno"
|
||||
},
|
||||
"speedtest": {
|
||||
"upload": "Upload",
|
||||
"download": "Download",
|
||||
"upload": "Prijenos",
|
||||
"download": "Preuzimanje",
|
||||
"ping": "Ping"
|
||||
},
|
||||
"portainer": {
|
||||
"running": "Running",
|
||||
"stopped": "Stopped",
|
||||
"total": "Total"
|
||||
"running": "Pokrenuto",
|
||||
"stopped": "Prekinuto",
|
||||
"total": "Ukupno"
|
||||
},
|
||||
"traefik": {
|
||||
"routers": "Routers",
|
||||
"services": "Services",
|
||||
"routers": "Ruteri",
|
||||
"services": "Usluge",
|
||||
"middleware": "Middleware"
|
||||
},
|
||||
"gotify": {
|
||||
"clients": "Clients",
|
||||
"messages": "Messages",
|
||||
"apps": "Applications"
|
||||
"clients": "Klijenti",
|
||||
"messages": "Poruke",
|
||||
"apps": "Programi"
|
||||
},
|
||||
"jackett": {
|
||||
"configured": "Configured",
|
||||
"errored": "Errored"
|
||||
"configured": "Konfigurirano",
|
||||
"errored": "S greškom"
|
||||
},
|
||||
"qbittorrent": {
|
||||
"download": "Download",
|
||||
"upload": "Upload",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
"download": "Preuzimanje",
|
||||
"upload": "Prijenos",
|
||||
"leech": "Krvopija",
|
||||
"seed": "Prijenos preuzetog sadržaja"
|
||||
},
|
||||
"mastodon": {
|
||||
"user_count": "Users",
|
||||
"status_count": "Posts",
|
||||
"domain_count": "Domains"
|
||||
"user_count": "Korisnici",
|
||||
"status_count": "Objave",
|
||||
"domain_count": "Domene"
|
||||
},
|
||||
"strelaysrv": {
|
||||
"numActiveSessions": "Sessions",
|
||||
"numConnections": "Connections",
|
||||
"dataRelayed": "Relayed",
|
||||
"transferRate": "Rate"
|
||||
"numActiveSessions": "Sesije",
|
||||
"numConnections": "Veze",
|
||||
"dataRelayed": "Proslijeđeno",
|
||||
"transferRate": "Stopa"
|
||||
},
|
||||
"authentik": {
|
||||
"users": "Users",
|
||||
"loginsLast24H": "Logins (24h)",
|
||||
"failedLoginsLast24H": "Failed Logins (24h)"
|
||||
},
|
||||
"proxmox": {
|
||||
"mem": "MEM",
|
||||
"cpu": "CPU",
|
||||
"lxc": "LXC",
|
||||
"vms": "VMs"
|
||||
},
|
||||
"unifi": {
|
||||
"users": "Users",
|
||||
"uptime": "System Uptime",
|
||||
"days": "Days",
|
||||
"wan": "WAN",
|
||||
"lan_users": "LAN Users",
|
||||
"wlan_users": "WLAN Users",
|
||||
"up": "UP",
|
||||
"down": "DOWN",
|
||||
"wait": "Please wait"
|
||||
},
|
||||
"plex": {
|
||||
"streams": "Active Streams",
|
||||
"movies": "Movies",
|
||||
"tv": "TV Shows"
|
||||
},
|
||||
"glances": {
|
||||
"cpu": "CPU",
|
||||
"mem": "MEM",
|
||||
"wait": "Please wait"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
"total": "Összes",
|
||||
"free": "Szabad",
|
||||
"used": "Használt",
|
||||
"load": "Terhelés"
|
||||
"load": "Terhelés",
|
||||
"cpu": "CPU"
|
||||
},
|
||||
"docker": {
|
||||
"rx": "RX",
|
||||
@@ -168,5 +169,37 @@
|
||||
"numConnections": "Connections",
|
||||
"dataRelayed": "Relayed",
|
||||
"transferRate": "Rate"
|
||||
},
|
||||
"authentik": {
|
||||
"users": "Users",
|
||||
"loginsLast24H": "Logins (24h)",
|
||||
"failedLoginsLast24H": "Failed Logins (24h)"
|
||||
},
|
||||
"proxmox": {
|
||||
"mem": "MEM",
|
||||
"cpu": "CPU",
|
||||
"lxc": "LXC",
|
||||
"vms": "VMs"
|
||||
},
|
||||
"unifi": {
|
||||
"users": "Users",
|
||||
"uptime": "System Uptime",
|
||||
"days": "Days",
|
||||
"wan": "WAN",
|
||||
"lan_users": "LAN Users",
|
||||
"wlan_users": "WLAN Users",
|
||||
"up": "UP",
|
||||
"down": "DOWN",
|
||||
"wait": "Please wait"
|
||||
},
|
||||
"plex": {
|
||||
"streams": "Active Streams",
|
||||
"movies": "Movies",
|
||||
"tv": "TV Shows"
|
||||
},
|
||||
"glances": {
|
||||
"cpu": "CPU",
|
||||
"mem": "MEM",
|
||||
"wait": "Please wait"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,8 @@
|
||||
"total": "Totale",
|
||||
"free": "Libero",
|
||||
"used": "In utilizzo",
|
||||
"load": "Load"
|
||||
"load": "Load",
|
||||
"cpu": "CPU"
|
||||
},
|
||||
"rutorrent": {
|
||||
"active": "Attivo",
|
||||
@@ -168,5 +169,37 @@
|
||||
"numConnections": "Connections",
|
||||
"dataRelayed": "Relayed",
|
||||
"transferRate": "Rate"
|
||||
},
|
||||
"authentik": {
|
||||
"users": "Users",
|
||||
"loginsLast24H": "Logins (24h)",
|
||||
"failedLoginsLast24H": "Failed Logins (24h)"
|
||||
},
|
||||
"proxmox": {
|
||||
"mem": "MEM",
|
||||
"cpu": "CPU",
|
||||
"lxc": "LXC",
|
||||
"vms": "VMs"
|
||||
},
|
||||
"unifi": {
|
||||
"users": "Users",
|
||||
"uptime": "System Uptime",
|
||||
"days": "Days",
|
||||
"wan": "WAN",
|
||||
"lan_users": "LAN Users",
|
||||
"wait": "Please wait",
|
||||
"wlan_users": "WLAN Users",
|
||||
"up": "UP",
|
||||
"down": "DOWN"
|
||||
},
|
||||
"plex": {
|
||||
"streams": "Active Streams",
|
||||
"movies": "Movies",
|
||||
"tv": "TV Shows"
|
||||
},
|
||||
"glances": {
|
||||
"cpu": "CPU",
|
||||
"mem": "MEM",
|
||||
"wait": "Please wait"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
"total": "Totalt",
|
||||
"free": "Ledig",
|
||||
"used": "Brukt",
|
||||
"load": "Last inn"
|
||||
"load": "Last inn",
|
||||
"cpu": "CPU"
|
||||
},
|
||||
"docker": {
|
||||
"rx": "Mottatt",
|
||||
@@ -168,5 +169,37 @@
|
||||
"numConnections": "Connections",
|
||||
"dataRelayed": "Relayed",
|
||||
"transferRate": "Rate"
|
||||
},
|
||||
"authentik": {
|
||||
"users": "Users",
|
||||
"loginsLast24H": "Logins (24h)",
|
||||
"failedLoginsLast24H": "Failed Logins (24h)"
|
||||
},
|
||||
"proxmox": {
|
||||
"mem": "MEM",
|
||||
"cpu": "CPU",
|
||||
"lxc": "LXC",
|
||||
"vms": "VMs"
|
||||
},
|
||||
"unifi": {
|
||||
"users": "Users",
|
||||
"uptime": "System Uptime",
|
||||
"days": "Days",
|
||||
"wan": "WAN",
|
||||
"lan_users": "LAN Users",
|
||||
"wlan_users": "WLAN Users",
|
||||
"up": "UP",
|
||||
"down": "DOWN",
|
||||
"wait": "Please wait"
|
||||
},
|
||||
"plex": {
|
||||
"streams": "Active Streams",
|
||||
"movies": "Movies",
|
||||
"tv": "TV Shows"
|
||||
},
|
||||
"glances": {
|
||||
"cpu": "CPU",
|
||||
"mem": "MEM",
|
||||
"wait": "Please wait"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
"total": "Totaal",
|
||||
"free": "Vrij",
|
||||
"used": "Gebruikt",
|
||||
"load": "Load"
|
||||
"load": "Load",
|
||||
"cpu": "CPU"
|
||||
},
|
||||
"docker": {
|
||||
"rx": "RX",
|
||||
@@ -168,5 +169,37 @@
|
||||
"numConnections": "Connections",
|
||||
"dataRelayed": "Relayed",
|
||||
"transferRate": "Rate"
|
||||
},
|
||||
"authentik": {
|
||||
"users": "Users",
|
||||
"loginsLast24H": "Logins (24h)",
|
||||
"failedLoginsLast24H": "Failed Logins (24h)"
|
||||
},
|
||||
"proxmox": {
|
||||
"mem": "MEM",
|
||||
"cpu": "CPU",
|
||||
"lxc": "LXC",
|
||||
"vms": "VMs"
|
||||
},
|
||||
"unifi": {
|
||||
"users": "Users",
|
||||
"lan_users": "LAN Users",
|
||||
"uptime": "System Uptime",
|
||||
"days": "Days",
|
||||
"wan": "WAN",
|
||||
"wlan_users": "WLAN Users",
|
||||
"up": "UP",
|
||||
"down": "DOWN",
|
||||
"wait": "Please wait"
|
||||
},
|
||||
"plex": {
|
||||
"streams": "Active Streams",
|
||||
"movies": "Movies",
|
||||
"tv": "TV Shows"
|
||||
},
|
||||
"glances": {
|
||||
"cpu": "CPU",
|
||||
"mem": "MEM",
|
||||
"wait": "Please wait"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
"used": "Użyte",
|
||||
"load": "Obciążenie",
|
||||
"total": "Całkowite",
|
||||
"free": "Wolne"
|
||||
"free": "Wolne",
|
||||
"cpu": "CPU"
|
||||
},
|
||||
"emby": {
|
||||
"no_active": "Brak aktywnych strumieni",
|
||||
@@ -168,5 +169,37 @@
|
||||
"numConnections": "Connections",
|
||||
"dataRelayed": "Relayed",
|
||||
"transferRate": "Rate"
|
||||
},
|
||||
"authentik": {
|
||||
"users": "Users",
|
||||
"loginsLast24H": "Logins (24h)",
|
||||
"failedLoginsLast24H": "Failed Logins (24h)"
|
||||
},
|
||||
"proxmox": {
|
||||
"mem": "MEM",
|
||||
"cpu": "CPU",
|
||||
"lxc": "LXC",
|
||||
"vms": "VMs"
|
||||
},
|
||||
"unifi": {
|
||||
"users": "Users",
|
||||
"uptime": "System Uptime",
|
||||
"days": "Days",
|
||||
"wan": "WAN",
|
||||
"lan_users": "LAN Users",
|
||||
"wlan_users": "WLAN Users",
|
||||
"up": "UP",
|
||||
"down": "DOWN",
|
||||
"wait": "Please wait"
|
||||
},
|
||||
"plex": {
|
||||
"streams": "Active Streams",
|
||||
"movies": "Movies",
|
||||
"tv": "TV Shows"
|
||||
},
|
||||
"glances": {
|
||||
"cpu": "CPU",
|
||||
"mem": "MEM",
|
||||
"wait": "Please wait"
|
||||
}
|
||||
}
|
||||
|
||||
205
public/locales/pt-BR/common.json
Normal file
@@ -0,0 +1,205 @@
|
||||
{
|
||||
"sabnzbd": {
|
||||
"timeleft": "Tempo restante",
|
||||
"rate": "Taxa",
|
||||
"queue": "Fila"
|
||||
},
|
||||
"rutorrent": {
|
||||
"active": "Ativo",
|
||||
"upload": "Envio",
|
||||
"download": "Download"
|
||||
},
|
||||
"portainer": {
|
||||
"total": "Total",
|
||||
"running": "Funcionando",
|
||||
"stopped": "Parado"
|
||||
},
|
||||
"coinmarketcap": {
|
||||
"7days": "7 Dias",
|
||||
"configure": "Configure uma ou mais criptomoedas para rastrear",
|
||||
"1hour": "1 Hora",
|
||||
"1day": "1 Dia",
|
||||
"30days": "30 Dias"
|
||||
},
|
||||
"strelaysrv": {
|
||||
"numConnections": "Conexões",
|
||||
"numActiveSessions": "Sessões",
|
||||
"dataRelayed": "Retransmitido",
|
||||
"transferRate": "Taxa"
|
||||
},
|
||||
"widget": {
|
||||
"missing_type": "Tipo de Widget ausente: {{type}}",
|
||||
"api_error": "Erro da API",
|
||||
"status": "Status"
|
||||
},
|
||||
"weather": {
|
||||
"current": "Localização atual",
|
||||
"allow": "Clique para permitir",
|
||||
"updating": "Atualizando",
|
||||
"wait": "Aguarde, por favor"
|
||||
},
|
||||
"search": {
|
||||
"placeholder": "Buscar…"
|
||||
},
|
||||
"resources": {
|
||||
"total": "Total",
|
||||
"free": "Livre",
|
||||
"used": "Usado",
|
||||
"load": "Carregamento",
|
||||
"cpu": "CPU"
|
||||
},
|
||||
"docker": {
|
||||
"rx": "Rx",
|
||||
"tx": "Tx",
|
||||
"mem": "Mem",
|
||||
"cpu": "CPU",
|
||||
"offline": "Desligado"
|
||||
},
|
||||
"emby": {
|
||||
"playing": "Reproduzindo",
|
||||
"transcoding": "Transcodificando",
|
||||
"bitrate": "Taxa de bits",
|
||||
"no_active": "Sem transmissões ativas"
|
||||
},
|
||||
"tautulli": {
|
||||
"playing": "Reproduzindo",
|
||||
"transcoding": "Transcodificando",
|
||||
"bitrate": "Taxa de bits",
|
||||
"no_active": "Sem transmissões ativas"
|
||||
},
|
||||
"nzbget": {
|
||||
"rate": "Taxa",
|
||||
"remaining": "Restando",
|
||||
"downloaded": "Baixado"
|
||||
},
|
||||
"transmission": {
|
||||
"download": "Download",
|
||||
"upload": "Upload",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"qbittorrent": {
|
||||
"download": "Download",
|
||||
"upload": "Upload",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"sonarr": {
|
||||
"wanted": "Desejado",
|
||||
"queued": "Na fila",
|
||||
"series": "Séries"
|
||||
},
|
||||
"radarr": {
|
||||
"wanted": "Desejado",
|
||||
"queued": "Na fila",
|
||||
"movies": "Filmes"
|
||||
},
|
||||
"lidarr": {
|
||||
"wanted": "Desejado",
|
||||
"queued": "Na fila",
|
||||
"albums": "Álbuns"
|
||||
},
|
||||
"readarr": {
|
||||
"wanted": "Desejado",
|
||||
"queued": "Na fila",
|
||||
"books": "Livros"
|
||||
},
|
||||
"bazarr": {
|
||||
"missingEpisodes": "Episódios Ausentes",
|
||||
"missingMovies": "Filmes Ausentes"
|
||||
},
|
||||
"ombi": {
|
||||
"pending": "Pendente",
|
||||
"approved": "Aprovado",
|
||||
"available": "Disponível"
|
||||
},
|
||||
"jellyseerr": {
|
||||
"pending": "Pendente",
|
||||
"approved": "Aprovado",
|
||||
"available": "Disponível"
|
||||
},
|
||||
"overseerr": {
|
||||
"pending": "Pendente",
|
||||
"approved": "Aprovado",
|
||||
"available": "Disponível"
|
||||
},
|
||||
"pihole": {
|
||||
"queries": "Consultas",
|
||||
"blocked": "Bloquado",
|
||||
"gravity": "Gravity"
|
||||
},
|
||||
"adguard": {
|
||||
"queries": "Consultas",
|
||||
"blocked": "Bloqueado",
|
||||
"filtered": "Filtrado",
|
||||
"latency": "Latência"
|
||||
},
|
||||
"speedtest": {
|
||||
"upload": "Envio",
|
||||
"download": "Receber",
|
||||
"ping": "Ping"
|
||||
},
|
||||
"traefik": {
|
||||
"routers": "Rotas",
|
||||
"services": "Serviços",
|
||||
"middleware": "Middleware"
|
||||
},
|
||||
"npm": {
|
||||
"enabled": "Habilitado",
|
||||
"disabled": "Desabilitado",
|
||||
"total": "Total"
|
||||
},
|
||||
"gotify": {
|
||||
"apps": "Aplicações",
|
||||
"clients": "Clientes",
|
||||
"messages": "Mensagens"
|
||||
},
|
||||
"prowlarr": {
|
||||
"enableIndexers": "Indexadores",
|
||||
"numberOfGrabs": "Grabs",
|
||||
"numberOfQueries": "Queries",
|
||||
"numberOfFailGrabs": "Fail Grabs",
|
||||
"numberOfFailQueries": "Fail Queries"
|
||||
},
|
||||
"jackett": {
|
||||
"configured": "Configurado",
|
||||
"errored": "Erro"
|
||||
},
|
||||
"mastodon": {
|
||||
"user_count": "Usuários",
|
||||
"status_count": "Postagens",
|
||||
"domain_count": "Domínios"
|
||||
},
|
||||
"authentik": {
|
||||
"users": "Users",
|
||||
"loginsLast24H": "Logins (24h)",
|
||||
"failedLoginsLast24H": "Failed Logins (24h)"
|
||||
},
|
||||
"proxmox": {
|
||||
"mem": "MEM",
|
||||
"cpu": "CPU",
|
||||
"lxc": "LXC",
|
||||
"vms": "VMs"
|
||||
},
|
||||
"unifi": {
|
||||
"users": "Users",
|
||||
"uptime": "System Uptime",
|
||||
"days": "Days",
|
||||
"wan": "WAN",
|
||||
"lan_users": "LAN Users",
|
||||
"wlan_users": "WLAN Users",
|
||||
"up": "UP",
|
||||
"down": "DOWN",
|
||||
"wait": "Please wait"
|
||||
},
|
||||
"plex": {
|
||||
"streams": "Active Streams",
|
||||
"movies": "Movies",
|
||||
"tv": "TV Shows"
|
||||
},
|
||||
"glances": {
|
||||
"cpu": "CPU",
|
||||
"mem": "MEM",
|
||||
"wait": "Please wait"
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,8 @@
|
||||
"total": "Total",
|
||||
"free": "Livre",
|
||||
"used": "Usado",
|
||||
"load": "Carregar"
|
||||
"load": "Carregar",
|
||||
"cpu": "CPU"
|
||||
},
|
||||
"docker": {
|
||||
"rx": "Rx",
|
||||
@@ -179,5 +180,37 @@
|
||||
"numConnections": "Conexões",
|
||||
"dataRelayed": "Retransmitido",
|
||||
"transferRate": "Taxa"
|
||||
},
|
||||
"authentik": {
|
||||
"loginsLast24H": "Logins (24h)",
|
||||
"failedLoginsLast24H": "Failed Logins (24h)",
|
||||
"users": "Users"
|
||||
},
|
||||
"proxmox": {
|
||||
"mem": "MEM",
|
||||
"cpu": "CPU",
|
||||
"lxc": "LXC",
|
||||
"vms": "VMs"
|
||||
},
|
||||
"unifi": {
|
||||
"users": "Users",
|
||||
"uptime": "System Uptime",
|
||||
"days": "Days",
|
||||
"wan": "WAN",
|
||||
"lan_users": "LAN Users",
|
||||
"wlan_users": "WLAN Users",
|
||||
"up": "UP",
|
||||
"down": "DOWN",
|
||||
"wait": "Please wait"
|
||||
},
|
||||
"plex": {
|
||||
"streams": "Active Streams",
|
||||
"movies": "Movies",
|
||||
"tv": "TV Shows"
|
||||
},
|
||||
"glances": {
|
||||
"cpu": "CPU",
|
||||
"mem": "MEM",
|
||||
"wait": "Please wait"
|
||||
}
|
||||
}
|
||||
|
||||
205
public/locales/ro/common.json
Normal file
@@ -0,0 +1,205 @@
|
||||
{
|
||||
"resources": {
|
||||
"used": "Utilizați",
|
||||
"load": "Sarcină",
|
||||
"total": "Total",
|
||||
"free": "Disponibili",
|
||||
"cpu": "CPU"
|
||||
},
|
||||
"docker": {
|
||||
"rx": "RX",
|
||||
"tx": "TX",
|
||||
"mem": "MEM",
|
||||
"cpu": "CPU",
|
||||
"offline": "Offline"
|
||||
},
|
||||
"jellyseerr": {
|
||||
"approved": "Aprobate",
|
||||
"available": "Disponibile",
|
||||
"pending": "În așteptare"
|
||||
},
|
||||
"overseerr": {
|
||||
"pending": "În așteptare",
|
||||
"approved": "Aprobate",
|
||||
"available": "Disponibile"
|
||||
},
|
||||
"pihole": {
|
||||
"queries": "Cereri",
|
||||
"blocked": "Blocate",
|
||||
"gravity": "Gravity"
|
||||
},
|
||||
"adguard": {
|
||||
"blocked": "Blocate",
|
||||
"filtered": "Filtrate",
|
||||
"queries": "Cereri",
|
||||
"latency": "Latentă"
|
||||
},
|
||||
"traefik": {
|
||||
"services": "Servicii",
|
||||
"middleware": "Middleware",
|
||||
"routers": "Routere"
|
||||
},
|
||||
"npm": {
|
||||
"enabled": "Activat",
|
||||
"disabled": "Dezactivat",
|
||||
"total": "Total"
|
||||
},
|
||||
"coinmarketcap": {
|
||||
"configure": "Configurați una sau mai multe criptomonede pe care să le urmăriți",
|
||||
"1hour": "1 Oră",
|
||||
"1day": "1 Zi",
|
||||
"7days": "7 Zile",
|
||||
"30days": "30 Zile"
|
||||
},
|
||||
"weather": {
|
||||
"allow": "Click pentru a permite",
|
||||
"updating": "Se actualizează",
|
||||
"current": "Locația Curentă",
|
||||
"wait": "Va rugăm așteptați"
|
||||
},
|
||||
"widget": {
|
||||
"missing_type": "Lipsește Tipul de Widget: {{type}}",
|
||||
"api_error": "Eroare API",
|
||||
"status": "Status"
|
||||
},
|
||||
"search": {
|
||||
"placeholder": "Caută…"
|
||||
},
|
||||
"tautulli": {
|
||||
"no_active": "Niciun stream activ",
|
||||
"playing": "Activ",
|
||||
"transcoding": "Transcodare",
|
||||
"bitrate": "Bitrate"
|
||||
},
|
||||
"nzbget": {
|
||||
"rate": "Rată",
|
||||
"remaining": "Rămas",
|
||||
"downloaded": "Descărcat"
|
||||
},
|
||||
"emby": {
|
||||
"playing": "Activ",
|
||||
"transcoding": "Transcodare",
|
||||
"bitrate": "Bitrate",
|
||||
"no_active": "Niciun stream activ"
|
||||
},
|
||||
"sabnzbd": {
|
||||
"rate": "Rată",
|
||||
"queue": "Coadă",
|
||||
"timeleft": "Timp rămas"
|
||||
},
|
||||
"transmission": {
|
||||
"leech": "Leech",
|
||||
"seed": "Seed",
|
||||
"download": "Descarcă",
|
||||
"upload": "Încarcă"
|
||||
},
|
||||
"rutorrent": {
|
||||
"active": "Activ",
|
||||
"upload": "Încarcă",
|
||||
"download": "Descarcă"
|
||||
},
|
||||
"qbittorrent": {
|
||||
"download": "Descarcă",
|
||||
"upload": "Încarcă",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"sonarr": {
|
||||
"wanted": "Dorite",
|
||||
"queued": "În coadă",
|
||||
"series": "Seriale"
|
||||
},
|
||||
"radarr": {
|
||||
"queued": "În coadă",
|
||||
"wanted": "Dorite",
|
||||
"movies": "Filme"
|
||||
},
|
||||
"lidarr": {
|
||||
"wanted": "Dorite",
|
||||
"queued": "În coadă",
|
||||
"albums": "Albume"
|
||||
},
|
||||
"readarr": {
|
||||
"wanted": "Dorite",
|
||||
"queued": "În coadă",
|
||||
"books": "Cărți"
|
||||
},
|
||||
"bazarr": {
|
||||
"missingEpisodes": "Episoade lipsă",
|
||||
"missingMovies": "Filme lipsă"
|
||||
},
|
||||
"ombi": {
|
||||
"pending": "În așteptare",
|
||||
"approved": "Aprobate",
|
||||
"available": "Disponibile"
|
||||
},
|
||||
"speedtest": {
|
||||
"upload": "Încarcă",
|
||||
"download": "Descarcă",
|
||||
"ping": "Ping"
|
||||
},
|
||||
"portainer": {
|
||||
"running": "Activ",
|
||||
"stopped": "Oprit",
|
||||
"total": "Total"
|
||||
},
|
||||
"gotify": {
|
||||
"apps": "Aplicații",
|
||||
"clients": "Clienți",
|
||||
"messages": "Mesaje"
|
||||
},
|
||||
"prowlarr": {
|
||||
"numberOfFailGrabs": "Descărcări eșuate",
|
||||
"numberOfFailQueries": "Cereri eșuate",
|
||||
"enableIndexers": "Indexatori",
|
||||
"numberOfGrabs": "Descărcate",
|
||||
"numberOfQueries": "Cereri"
|
||||
},
|
||||
"jackett": {
|
||||
"configured": "Configurat",
|
||||
"errored": "Cu erori"
|
||||
},
|
||||
"strelaysrv": {
|
||||
"numActiveSessions": "Sesiuni",
|
||||
"numConnections": "Conexiuni",
|
||||
"dataRelayed": "Retransmise",
|
||||
"transferRate": "Rată"
|
||||
},
|
||||
"mastodon": {
|
||||
"user_count": "Utilizatori",
|
||||
"status_count": "Postări",
|
||||
"domain_count": "Domenii"
|
||||
},
|
||||
"authentik": {
|
||||
"users": "Utilizatori",
|
||||
"loginsLast24H": "Autentificări (24h)",
|
||||
"failedLoginsLast24H": "Conectări eșuate (24h)"
|
||||
},
|
||||
"proxmox": {
|
||||
"vms": "VMs",
|
||||
"mem": "MEM",
|
||||
"cpu": "CPU",
|
||||
"lxc": "LXC"
|
||||
},
|
||||
"unifi": {
|
||||
"users": "Users",
|
||||
"uptime": "System Uptime",
|
||||
"days": "Days",
|
||||
"wan": "WAN",
|
||||
"lan_users": "LAN Users",
|
||||
"wlan_users": "WLAN Users",
|
||||
"up": "UP",
|
||||
"down": "DOWN",
|
||||
"wait": "Please wait"
|
||||
},
|
||||
"plex": {
|
||||
"streams": "Active Streams",
|
||||
"movies": "Movies",
|
||||
"tv": "TV Shows"
|
||||
},
|
||||
"glances": {
|
||||
"cpu": "CPU",
|
||||
"mem": "MEM",
|
||||
"wait": "Please wait"
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,8 @@
|
||||
"total": "Всего",
|
||||
"free": "Свободно",
|
||||
"used": "Использовано",
|
||||
"load": "Load"
|
||||
"load": "Load",
|
||||
"cpu": "CPU"
|
||||
},
|
||||
"docker": {
|
||||
"rx": "Rx",
|
||||
@@ -168,5 +169,37 @@
|
||||
"dataRelayed": "Relayed",
|
||||
"transferRate": "Rate",
|
||||
"numActiveSessions": "Sessions"
|
||||
},
|
||||
"authentik": {
|
||||
"users": "Users",
|
||||
"loginsLast24H": "Logins (24h)",
|
||||
"failedLoginsLast24H": "Failed Logins (24h)"
|
||||
},
|
||||
"proxmox": {
|
||||
"mem": "MEM",
|
||||
"cpu": "CPU",
|
||||
"lxc": "LXC",
|
||||
"vms": "VMs"
|
||||
},
|
||||
"unifi": {
|
||||
"users": "Users",
|
||||
"uptime": "System Uptime",
|
||||
"days": "Days",
|
||||
"wan": "WAN",
|
||||
"lan_users": "LAN Users",
|
||||
"wlan_users": "WLAN Users",
|
||||
"up": "UP",
|
||||
"down": "DOWN",
|
||||
"wait": "Please wait"
|
||||
},
|
||||
"plex": {
|
||||
"streams": "Active Streams",
|
||||
"movies": "Movies",
|
||||
"tv": "TV Shows"
|
||||
},
|
||||
"glances": {
|
||||
"cpu": "CPU",
|
||||
"mem": "MEM",
|
||||
"wait": "Please wait"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
"load": "Laddar",
|
||||
"total": "Total",
|
||||
"free": "Ledigt",
|
||||
"used": "Använt"
|
||||
"used": "Använt",
|
||||
"cpu": "CPU"
|
||||
},
|
||||
"docker": {
|
||||
"rx": "RX",
|
||||
@@ -168,5 +169,37 @@
|
||||
"numConnections": "Anslutningar",
|
||||
"dataRelayed": "Relayed",
|
||||
"transferRate": "Rate"
|
||||
},
|
||||
"authentik": {
|
||||
"users": "Users",
|
||||
"loginsLast24H": "Logins (24h)",
|
||||
"failedLoginsLast24H": "Failed Logins (24h)"
|
||||
},
|
||||
"proxmox": {
|
||||
"mem": "MEM",
|
||||
"cpu": "CPU",
|
||||
"lxc": "LXC",
|
||||
"vms": "VMs"
|
||||
},
|
||||
"unifi": {
|
||||
"users": "Users",
|
||||
"uptime": "System Uptime",
|
||||
"days": "Days",
|
||||
"wan": "WAN",
|
||||
"lan_users": "LAN Users",
|
||||
"wlan_users": "WLAN Users",
|
||||
"up": "UP",
|
||||
"down": "DOWN",
|
||||
"wait": "Please wait"
|
||||
},
|
||||
"plex": {
|
||||
"streams": "Active Streams",
|
||||
"movies": "Movies",
|
||||
"tv": "TV Shows"
|
||||
},
|
||||
"glances": {
|
||||
"cpu": "CPU",
|
||||
"mem": "MEM",
|
||||
"wait": "Please wait"
|
||||
}
|
||||
}
|
||||
|
||||
205
public/locales/te/common.json
Normal file
@@ -0,0 +1,205 @@
|
||||
{
|
||||
"readarr": {
|
||||
"books": "పుస్తకాలు",
|
||||
"wanted": "కావలెను",
|
||||
"queued": "క్యూయూఎడ్"
|
||||
},
|
||||
"adguard": {
|
||||
"blocked": "నిరోధించబడింది",
|
||||
"filtered": "ఫిల్టర్ చేయబడింది",
|
||||
"latency": "జాప్యం",
|
||||
"queries": "ప్రశ్నలు"
|
||||
},
|
||||
"strelaysrv": {
|
||||
"numActiveSessions": "సెషన్స్",
|
||||
"numConnections": "కనెక్షన్లు",
|
||||
"dataRelayed": "రెలయెడఁ",
|
||||
"transferRate": "రేటు"
|
||||
},
|
||||
"widget": {
|
||||
"missing_type": "విడ్జెట్ లేదు: {{type}}",
|
||||
"api_error": "API లోపం",
|
||||
"status": "హోదా"
|
||||
},
|
||||
"weather": {
|
||||
"current": "ప్రస్తుత స్తలం",
|
||||
"allow": "అనుమతించడానికి క్లిక్ చేయండి",
|
||||
"updating": "నవీకరిస్తోంది",
|
||||
"wait": "దయచేసి వేచి ఉండండి"
|
||||
},
|
||||
"search": {
|
||||
"placeholder": "వెతకండి…"
|
||||
},
|
||||
"resources": {
|
||||
"cpu": "సీపియూ",
|
||||
"total": "మొత్తం",
|
||||
"free": "మిగిలింది",
|
||||
"used": "ఉపయోగించబడిన",
|
||||
"load": "లోడ్"
|
||||
},
|
||||
"docker": {
|
||||
"rx": "RX",
|
||||
"tx": "TX",
|
||||
"mem": "MEM",
|
||||
"cpu": "సీపియూ",
|
||||
"offline": "ఆఫ్లైన్"
|
||||
},
|
||||
"emby": {
|
||||
"playing": "ఆడుతున్నారు",
|
||||
"transcoding": "ట్రాన్స్కోడింగ్",
|
||||
"bitrate": "బిట్రేట్",
|
||||
"no_active": "యాక్టివ్ స్ట్రీమ్లు లేవు"
|
||||
},
|
||||
"tautulli": {
|
||||
"playing": "ఆడుతున్నారు",
|
||||
"transcoding": "ట్రాన్స్కోడింగ్",
|
||||
"bitrate": "బిట్రేట్",
|
||||
"no_active": "యాక్టివ్ స్ట్రీమ్లు లేవు"
|
||||
},
|
||||
"nzbget": {
|
||||
"rate": "రేట్",
|
||||
"remaining": "మిగిలింది",
|
||||
"downloaded": "డౌన్లోడ్ చేయబడింది"
|
||||
},
|
||||
"sabnzbd": {
|
||||
"rate": "రేట్",
|
||||
"queue": "వరుస",
|
||||
"timeleft": "మిగిలి వున్న సమయం"
|
||||
},
|
||||
"rutorrent": {
|
||||
"active": "చురుకుగా",
|
||||
"upload": "అప్లోడ్",
|
||||
"download": "డౌన్లోడ్"
|
||||
},
|
||||
"transmission": {
|
||||
"download": "డౌన్లోడ్",
|
||||
"upload": "అప్లోడ్",
|
||||
"leech": "జలగ",
|
||||
"seed": "సీడ్"
|
||||
},
|
||||
"qbittorrent": {
|
||||
"download": "డౌన్లోడ్",
|
||||
"upload": "అప్లోడ్",
|
||||
"leech": "లీచ్",
|
||||
"seed": "సీడ్"
|
||||
},
|
||||
"sonarr": {
|
||||
"wanted": "కావలెను",
|
||||
"queued": "క్యూయూఎడ్",
|
||||
"series": "సిరీస్"
|
||||
},
|
||||
"radarr": {
|
||||
"wanted": "కావలెను",
|
||||
"queued": "క్యూయూఎడ్",
|
||||
"movies": "సినిమాలు"
|
||||
},
|
||||
"lidarr": {
|
||||
"wanted": "కావలెను",
|
||||
"queued": "క్యూయూఎడ్",
|
||||
"albums": "ఆల్బములు"
|
||||
},
|
||||
"bazarr": {
|
||||
"missingEpisodes": "ఎపిసోడ్లు లేవు",
|
||||
"missingMovies": "సినిమాలు లేవు"
|
||||
},
|
||||
"ombi": {
|
||||
"pending": "పెండింగ్",
|
||||
"approved": "ఆమోదించబడింది",
|
||||
"available": "అందుబాటులో వున్నవి"
|
||||
},
|
||||
"jellyseerr": {
|
||||
"pending": "పెండింగ్",
|
||||
"approved": "ఆమోదించబడింది",
|
||||
"available": "అందుబాటులో"
|
||||
},
|
||||
"overseerr": {
|
||||
"pending": "పెండింగ్",
|
||||
"approved": "ఆమోదించబడింది",
|
||||
"available": "అందుబాటులో"
|
||||
},
|
||||
"pihole": {
|
||||
"queries": "ప్రశ్నలు",
|
||||
"blocked": "నిరోధించబడింది",
|
||||
"gravity": "గురుత్వాకర్షణ"
|
||||
},
|
||||
"speedtest": {
|
||||
"upload": "అప్లోడ్",
|
||||
"download": "డౌన్లోడ్",
|
||||
"ping": "పింగ్"
|
||||
},
|
||||
"portainer": {
|
||||
"running": "నడుస్తున్నవి",
|
||||
"stopped": "ఆగిపోయినవి",
|
||||
"total": "మొత్తం"
|
||||
},
|
||||
"traefik": {
|
||||
"routers": "రౌటర్లు",
|
||||
"services": "సేవలు",
|
||||
"middleware": "మిడిల్వేర్"
|
||||
},
|
||||
"npm": {
|
||||
"enabled": "ప్రారంభించబడింది",
|
||||
"disabled": "డిసేబ్లెడ్",
|
||||
"total": "మొత్తం"
|
||||
},
|
||||
"coinmarketcap": {
|
||||
"configure": "ట్రాక్ చేయడానికి ఒకటి లేదా అంతకంటే ఎక్కువ క్రిప్టో కరెన్సీలను కాన్ఫిగర్ చేయండి",
|
||||
"1hour": "1 గంట",
|
||||
"1day": "1 రోజు",
|
||||
"7days": "7 రోజులు",
|
||||
"30days": "30 రోజులు"
|
||||
},
|
||||
"gotify": {
|
||||
"apps": "అప్లికేషన్లు",
|
||||
"clients": "ఖాతాదారులు",
|
||||
"messages": "సందేశాలు"
|
||||
},
|
||||
"prowlarr": {
|
||||
"enableIndexers": "సూచికలు",
|
||||
"numberOfGrabs": "గ్రాబ్స్",
|
||||
"numberOfQueries": "ప్రశ్నలు",
|
||||
"numberOfFailGrabs": "ఫెయిల్ గ్రాబ్స్",
|
||||
"numberOfFailQueries": "విఫలమైన ప్రశ్నలు"
|
||||
},
|
||||
"jackett": {
|
||||
"configured": "కాన్ఫిగర్ చేయబడింది",
|
||||
"errored": "పొరపాటు జరిగింది"
|
||||
},
|
||||
"mastodon": {
|
||||
"user_count": "వినియోగదారులు",
|
||||
"status_count": "పోస్ట్లు",
|
||||
"domain_count": "డొమైన్లు"
|
||||
},
|
||||
"authentik": {
|
||||
"users": "వినియోగదారులు",
|
||||
"loginsLast24H": "లాగిన్లు (24గం)",
|
||||
"failedLoginsLast24H": "విఫలమైన లాగిన్లు (24గం)"
|
||||
},
|
||||
"proxmox": {
|
||||
"mem": "MEM",
|
||||
"cpu": "సీపియూ",
|
||||
"lxc": "LXC",
|
||||
"vms": "విఎంలు"
|
||||
},
|
||||
"unifi": {
|
||||
"users": "వినియోగదారులు",
|
||||
"uptime": "సిస్టమ్ సమయము",
|
||||
"days": "రోజులు",
|
||||
"wan": "WAN",
|
||||
"lan_users": "LAN వినియోగదారులు",
|
||||
"wlan_users": "WLAN వినియోగదారులు",
|
||||
"up": "అప్",
|
||||
"down": "డౌన్",
|
||||
"wait": "దయచేసి వేచి ఉండండి"
|
||||
},
|
||||
"plex": {
|
||||
"streams": "యాక్టివ్ స్ట్రీమ్లు",
|
||||
"movies": "సినిమాలు",
|
||||
"tv": "దూరదర్శిని కార్యక్రమాలు"
|
||||
},
|
||||
"glances": {
|
||||
"cpu": "CPU",
|
||||
"mem": "MEM",
|
||||
"wait": "Please wait"
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,8 @@
|
||||
"total": "Tổng",
|
||||
"free": "Dư",
|
||||
"used": "Đã dùng",
|
||||
"load": "Load"
|
||||
"load": "Load",
|
||||
"cpu": "CPU"
|
||||
},
|
||||
"docker": {
|
||||
"rx": "RX",
|
||||
@@ -168,5 +169,37 @@
|
||||
"numConnections": "Connections",
|
||||
"dataRelayed": "Relayed",
|
||||
"transferRate": "Rate"
|
||||
},
|
||||
"authentik": {
|
||||
"users": "Users",
|
||||
"loginsLast24H": "Logins (24h)",
|
||||
"failedLoginsLast24H": "Failed Logins (24h)"
|
||||
},
|
||||
"proxmox": {
|
||||
"mem": "MEM",
|
||||
"cpu": "CPU",
|
||||
"lxc": "LXC",
|
||||
"vms": "VMs"
|
||||
},
|
||||
"unifi": {
|
||||
"users": "Users",
|
||||
"uptime": "System Uptime",
|
||||
"days": "Days",
|
||||
"wan": "WAN",
|
||||
"lan_users": "LAN Users",
|
||||
"wlan_users": "WLAN Users",
|
||||
"up": "UP",
|
||||
"down": "DOWN",
|
||||
"wait": "Please wait"
|
||||
},
|
||||
"plex": {
|
||||
"streams": "Active Streams",
|
||||
"movies": "Movies",
|
||||
"tv": "TV Shows"
|
||||
},
|
||||
"glances": {
|
||||
"cpu": "CPU",
|
||||
"mem": "MEM",
|
||||
"wait": "Please wait"
|
||||
}
|
||||
}
|
||||
|
||||
205
public/locales/yue/common.json
Normal file
@@ -0,0 +1,205 @@
|
||||
{
|
||||
"emby": {
|
||||
"transcoding": "轉碼緊",
|
||||
"bitrate": "比特率",
|
||||
"playing": "播放緊",
|
||||
"no_active": "無任何活動"
|
||||
},
|
||||
"tautulli": {
|
||||
"playing": "播放緊",
|
||||
"no_active": "無任何活動",
|
||||
"transcoding": "轉碼緊",
|
||||
"bitrate": "比特率"
|
||||
},
|
||||
"transmission": {
|
||||
"download": "下載速度",
|
||||
"upload": "上傳速度",
|
||||
"leech": "下載緊",
|
||||
"seed": "做種緊"
|
||||
},
|
||||
"widget": {
|
||||
"missing_type": "缺少小部件類型:{{type}}",
|
||||
"api_error": "API 錯誤",
|
||||
"status": "狀況"
|
||||
},
|
||||
"weather": {
|
||||
"current": "依家位置",
|
||||
"allow": "點擊允許",
|
||||
"updating": "更新緊",
|
||||
"wait": "請稍後"
|
||||
},
|
||||
"search": {
|
||||
"placeholder": "搜索緊…"
|
||||
},
|
||||
"resources": {
|
||||
"total": "全部",
|
||||
"free": "剩餘",
|
||||
"used": "用咗",
|
||||
"load": "負荷",
|
||||
"cpu": "CPU"
|
||||
},
|
||||
"docker": {
|
||||
"rx": "接收",
|
||||
"tx": "發送",
|
||||
"mem": "內存",
|
||||
"cpu": "處理器",
|
||||
"offline": "離線"
|
||||
},
|
||||
"nzbget": {
|
||||
"rate": "速度",
|
||||
"remaining": "剩餘",
|
||||
"downloaded": "下載咗"
|
||||
},
|
||||
"sabnzbd": {
|
||||
"rate": "速度",
|
||||
"queue": "隊列",
|
||||
"timeleft": "用時"
|
||||
},
|
||||
"rutorrent": {
|
||||
"active": "激活",
|
||||
"upload": "上傳",
|
||||
"download": "下載"
|
||||
},
|
||||
"qbittorrent": {
|
||||
"download": "下載速度",
|
||||
"upload": "上傳速度",
|
||||
"leech": "下載緊",
|
||||
"seed": "做種緊"
|
||||
},
|
||||
"sonarr": {
|
||||
"wanted": "想睇",
|
||||
"queued": "排緊隊",
|
||||
"series": "電視劇"
|
||||
},
|
||||
"radarr": {
|
||||
"wanted": "想睇",
|
||||
"queued": "排緊隊",
|
||||
"movies": "電影"
|
||||
},
|
||||
"lidarr": {
|
||||
"wanted": "想睇",
|
||||
"queued": "排緊隊",
|
||||
"albums": "專輯"
|
||||
},
|
||||
"readarr": {
|
||||
"wanted": "想睇",
|
||||
"queued": "排緊隊",
|
||||
"books": "書"
|
||||
},
|
||||
"bazarr": {
|
||||
"missingEpisodes": "缺少嘅劇集",
|
||||
"missingMovies": "缺少電影"
|
||||
},
|
||||
"ombi": {
|
||||
"pending": "待定",
|
||||
"approved": "批准",
|
||||
"available": "可用"
|
||||
},
|
||||
"jellyseerr": {
|
||||
"pending": "提交咗",
|
||||
"approved": "批准咗",
|
||||
"available": "可睇嘅總量"
|
||||
},
|
||||
"overseerr": {
|
||||
"pending": "待定",
|
||||
"approved": "批准",
|
||||
"available": "可用"
|
||||
},
|
||||
"pihole": {
|
||||
"queries": "查詢",
|
||||
"blocked": "封鎖",
|
||||
"gravity": "重力"
|
||||
},
|
||||
"adguard": {
|
||||
"queries": "查詢",
|
||||
"blocked": "封鎖",
|
||||
"filtered": "過濾",
|
||||
"latency": "延遲"
|
||||
},
|
||||
"speedtest": {
|
||||
"upload": "上傳速率",
|
||||
"download": "下載速率",
|
||||
"ping": "Ping值"
|
||||
},
|
||||
"portainer": {
|
||||
"running": "運行緊",
|
||||
"stopped": "暫停",
|
||||
"total": "全部"
|
||||
},
|
||||
"traefik": {
|
||||
"routers": "路由器",
|
||||
"services": "服務項",
|
||||
"middleware": "中間件"
|
||||
},
|
||||
"coinmarketcap": {
|
||||
"1day": "1 日",
|
||||
"configure": "配置一個或多個加密貨幣以進行跟蹤",
|
||||
"1hour": "1 個鐘",
|
||||
"7days": "7 日",
|
||||
"30days": "30日"
|
||||
},
|
||||
"npm": {
|
||||
"enabled": "啟用",
|
||||
"disabled": "停用咗",
|
||||
"total": "全部"
|
||||
},
|
||||
"gotify": {
|
||||
"apps": "應用",
|
||||
"clients": "客戶端",
|
||||
"messages": "消息"
|
||||
},
|
||||
"prowlarr": {
|
||||
"enableIndexers": "索引",
|
||||
"numberOfGrabs": "抓住",
|
||||
"numberOfQueries": "查詢",
|
||||
"numberOfFailGrabs": "失敗抓取",
|
||||
"numberOfFailQueries": "查詢失敗"
|
||||
},
|
||||
"jackett": {
|
||||
"configured": "配置",
|
||||
"errored": "已錯誤"
|
||||
},
|
||||
"strelaysrv": {
|
||||
"numActiveSessions": "會話",
|
||||
"numConnections": "連接",
|
||||
"dataRelayed": "傳遞",
|
||||
"transferRate": "速度"
|
||||
},
|
||||
"mastodon": {
|
||||
"user_count": "用戶",
|
||||
"status_count": "職位",
|
||||
"domain_count": "域"
|
||||
},
|
||||
"authentik": {
|
||||
"users": "用戶",
|
||||
"loginsLast24H": "登錄( 24小时)",
|
||||
"failedLoginsLast24H": "登錄失敗( 24鐘頭)"
|
||||
},
|
||||
"proxmox": {
|
||||
"mem": "MEM",
|
||||
"cpu": "CPU",
|
||||
"lxc": "LXC",
|
||||
"vms": "VMs"
|
||||
},
|
||||
"unifi": {
|
||||
"users": "Users",
|
||||
"uptime": "System Uptime",
|
||||
"days": "Days",
|
||||
"wan": "WAN",
|
||||
"lan_users": "LAN Users",
|
||||
"wlan_users": "WLAN Users",
|
||||
"up": "UP",
|
||||
"down": "DOWN",
|
||||
"wait": "Please wait"
|
||||
},
|
||||
"plex": {
|
||||
"streams": "Active Streams",
|
||||
"movies": "Movies",
|
||||
"tv": "TV Shows"
|
||||
},
|
||||
"glances": {
|
||||
"cpu": "CPU",
|
||||
"mem": "MEM",
|
||||
"wait": "Please wait"
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,8 @@
|
||||
"total": "共",
|
||||
"free": "空闲",
|
||||
"used": "已用",
|
||||
"load": "负载"
|
||||
"load": "负载",
|
||||
"cpu": "CPU"
|
||||
},
|
||||
"docker": {
|
||||
"rx": "接收",
|
||||
@@ -91,7 +92,7 @@
|
||||
"current": "当前定位",
|
||||
"allow": "点击并允许",
|
||||
"updating": "更新中",
|
||||
"wait": "请稍后"
|
||||
"wait": "请稍候"
|
||||
},
|
||||
"overseerr": {
|
||||
"pending": "待办",
|
||||
@@ -161,12 +162,44 @@
|
||||
"mastodon": {
|
||||
"user_count": "用户",
|
||||
"status_count": "Posts",
|
||||
"domain_count": "Domains"
|
||||
"domain_count": "域"
|
||||
},
|
||||
"strelaysrv": {
|
||||
"numActiveSessions": "Sessions",
|
||||
"dataRelayed": "Relayed",
|
||||
"numConnections": "Connections",
|
||||
"transferRate": "Rate"
|
||||
"numActiveSessions": "会话",
|
||||
"dataRelayed": "中继",
|
||||
"numConnections": "连接",
|
||||
"transferRate": "速度"
|
||||
},
|
||||
"authentik": {
|
||||
"users": "用户",
|
||||
"loginsLast24H": "登录 (24h)",
|
||||
"failedLoginsLast24H": "登录失败 (24h)"
|
||||
},
|
||||
"proxmox": {
|
||||
"mem": "内存",
|
||||
"cpu": "处理器",
|
||||
"lxc": "LXC",
|
||||
"vms": "VMs"
|
||||
},
|
||||
"unifi": {
|
||||
"users": "Users",
|
||||
"uptime": "System Uptime",
|
||||
"days": "Days",
|
||||
"wan": "WAN",
|
||||
"lan_users": "LAN Users",
|
||||
"wlan_users": "WLAN Users",
|
||||
"up": "UP",
|
||||
"down": "DOWN",
|
||||
"wait": "请稍候"
|
||||
},
|
||||
"plex": {
|
||||
"streams": "Active Streams",
|
||||
"movies": "Movies",
|
||||
"tv": "TV Shows"
|
||||
},
|
||||
"glances": {
|
||||
"cpu": "CPU",
|
||||
"mem": "MEM",
|
||||
"wait": "Please wait"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,8 @@
|
||||
"total": "Total",
|
||||
"free": "Free",
|
||||
"used": "Used",
|
||||
"load": "Load"
|
||||
"load": "Load",
|
||||
"cpu": "CPU"
|
||||
},
|
||||
"nzbget": {
|
||||
"rate": "Rate",
|
||||
@@ -168,5 +169,37 @@
|
||||
"numConnections": "Connections",
|
||||
"dataRelayed": "Relayed",
|
||||
"transferRate": "Rate"
|
||||
},
|
||||
"authentik": {
|
||||
"users": "Users",
|
||||
"loginsLast24H": "Logins (24h)",
|
||||
"failedLoginsLast24H": "Failed Logins (24h)"
|
||||
},
|
||||
"proxmox": {
|
||||
"mem": "MEM",
|
||||
"cpu": "CPU",
|
||||
"lxc": "LXC",
|
||||
"vms": "VMs"
|
||||
},
|
||||
"unifi": {
|
||||
"users": "Users",
|
||||
"uptime": "System Uptime",
|
||||
"days": "Days",
|
||||
"wan": "WAN",
|
||||
"lan_users": "LAN Users",
|
||||
"wlan_users": "WLAN Users",
|
||||
"up": "UP",
|
||||
"down": "DOWN",
|
||||
"wait": "Please wait"
|
||||
},
|
||||
"plex": {
|
||||
"streams": "Active Streams",
|
||||
"movies": "Movies",
|
||||
"tv": "TV Shows"
|
||||
},
|
||||
"glances": {
|
||||
"cpu": "CPU",
|
||||
"mem": "MEM",
|
||||
"wait": "Please wait"
|
||||
}
|
||||
}
|
||||
|
||||
BIN
public/mstile-144x144.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
public/mstile-150x150.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
public/mstile-310x150.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
public/mstile-310x310.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
public/mstile-70x70.png
Normal file
|
After Width: | Height: | Size: 958 B |
23
public/safari-pinned-tab.svg
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="700.000000pt" height="700.000000pt" viewBox="0 0 700.000000 700.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.14, written by Peter Selinger 2001-2017
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,700.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M1682 5688 c-141 -24 -253 -147 -287 -316 -5 -24 -10 -3658 -5 -4029
|
||||
0 -33 0 -33 168 108 92 77 178 151 192 162 14 12 75 64 136 114 62 51 114 99
|
||||
116 105 2 7 4 209 3 449 l-1 436 276 0 c151 0 277 -4 280 -8 3 -4 5 -98 4
|
||||
-208 0 -111 2 -201 4 -201 3 0 117 95 132 110 3 3 73 61 155 130 168 140 167
|
||||
139 250 209 51 43 62 48 75 37 13 -12 524 -441 654 -550 28 -22 68 -57 91 -76
|
||||
23 -19 66 -55 95 -80 30 -25 86 -72 125 -105 38 -33 140 -118 225 -190 85 -71
|
||||
166 -139 180 -151 14 -13 70 -60 124 -106 55 -46 133 -111 174 -145 41 -35 77
|
||||
-61 80 -59 4 2 7 607 7 1344 -1 1197 1 1341 15 1344 8 2 163 3 343 3 l328 0
|
||||
-4 685 c-1 377 -6 695 -9 706 -26 90 -93 186 -161 233 -86 58 30 55 -1924 55
|
||||
-991 1 -1819 -2 -1841 -6z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -1,10 +1,13 @@
|
||||
import ErrorBoundary from "components/errorboundry";
|
||||
import List from "components/bookmarks/list";
|
||||
|
||||
export default function BookmarksGroup({ group }) {
|
||||
return (
|
||||
<div key={group.name} className="basis-full md:basis-1/2 lg:basis-1/3 xl:basis-1/4 flex-1 p-1">
|
||||
<div key={group.name} className="basis-full md:basis-1/2 lg:basis-1/3 xl:basis-1/4 flex-1">
|
||||
<h2 className="text-theme-800 dark:text-theme-300 text-xl font-medium">{group.name}</h2>
|
||||
<List bookmarks={group.bookmarks} />
|
||||
<ErrorBoundary>
|
||||
<List bookmarks={group.bookmarks} />
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useContext } from "react";
|
||||
|
||||
import { SettingsContext } from "utils/settings-context";
|
||||
import { SettingsContext } from "utils/contexts/settings";
|
||||
|
||||
export default function Item({ bookmark }) {
|
||||
const { hostname } = new URL(bookmark.href);
|
||||
@@ -12,7 +12,7 @@ export default function Item({ bookmark }) {
|
||||
href={bookmark.href}
|
||||
title={bookmark.name}
|
||||
target={settings.target ?? "_blank"}
|
||||
className="block w-full text-left mb-3 cursor-pointer rounded-md font-medium text-theme-700 hover:text-theme-700 dark:text-theme-200 dark:hover:text-theme-300 shadow-md shadow-black/10 dark:shadow-black/20 bg-white/50 hover:bg-theme-300/10 dark:bg-white/10 dark:hover:bg-white/20"
|
||||
className="block w-full text-left cursor-pointer transition-all h-15 mb-3 rounded-md font-medium text-theme-700 dark:text-theme-200 dark:hover:text-theme-300 shadow-md shadow-theme-900/10 dark:shadow-theme-900/20 bg-theme-100/20 hover:bg-theme-300/20 dark:bg-white/5 dark:hover:bg-white/10"
|
||||
>
|
||||
<div className="flex">
|
||||
<div className="flex-shrink-0 flex items-center justify-center w-11 bg-theme-500/10 dark:bg-theme-900/50 text-theme-700 hover:text-theme-700 dark:text-theme-200 text-sm font-medium rounded-l-md">
|
||||
@@ -20,7 +20,7 @@ export default function Item({ bookmark }) {
|
||||
</div>
|
||||
<div className="flex-1 flex items-center justify-between rounded-r-md ">
|
||||
<div className="flex-1 grow pl-3 py-2 text-xs">{bookmark.name}</div>
|
||||
<div className="px-2 py-2 truncate text-theme-500 dark:text-theme-400 opacity-50 text-xs">{hostname}</div>
|
||||
<div className="px-2 py-2 truncate text-theme-500 dark:text-theme-300 text-xs">{hostname}</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
40
src/components/errorboundry.jsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import React from "react";
|
||||
|
||||
export default class ErrorBoundary extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { error: null, errorInfo: null };
|
||||
}
|
||||
|
||||
componentDidCatch(error, errorInfo) {
|
||||
// Catch errors in any components below and re-render with error message
|
||||
this.setState({
|
||||
error,
|
||||
errorInfo,
|
||||
});
|
||||
|
||||
// You can also log error messages to an error reporting service here
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error, errorInfo);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { error, errorInfo } = this.state;
|
||||
if (errorInfo) {
|
||||
// Error path
|
||||
return (
|
||||
<div className="inline-block text-sm bg-rose-100 text-rose-900 dark:bg-rose-900 dark:text-rose-100 rounded-md p-2 m-1">
|
||||
<div className="font-medium mb-1">Something went wrong.</div>
|
||||
<details className="text-xs font-mono whitespace-pre">
|
||||
<summary>{error && error.toString()}</summary>
|
||||
{errorInfo.componentStack}
|
||||
</details>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Normally, just render children
|
||||
const { children } = this.props;
|
||||
return children;
|
||||
}
|
||||
}
|
||||
114
src/components/favicon.jsx
Normal file
@@ -0,0 +1,114 @@
|
||||
/* eslint-disable @next/next/no-img-element */
|
||||
/* eslint-disable jsx-a11y/alt-text */
|
||||
import { useRef, useEffect, useContext } from "react";
|
||||
|
||||
import themes from "utils/styles/themes";
|
||||
import { ColorContext } from "utils/contexts/color";
|
||||
|
||||
export function Svg({ svgRef = null }) {
|
||||
const { color } = useContext(ColorContext);
|
||||
|
||||
const { iconStart, iconEnd } = themes[color];
|
||||
|
||||
return (
|
||||
<svg
|
||||
ref={svgRef}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 1024 1024"
|
||||
style={{
|
||||
enableBackground: "new 0 0 1024 1024",
|
||||
}}
|
||||
xmlSpace="preserve"
|
||||
className="w-full h-full"
|
||||
>
|
||||
<style>
|
||||
{
|
||||
".st0{display:none}.st3{stroke-linecap:square}.st3,.st4{fill:none;stroke:#fff;stroke-miterlimit:10}.st6{display:inline;fill:#333}.st7{fill:#fff}"
|
||||
}
|
||||
</style>
|
||||
<g id="Icon">
|
||||
<path
|
||||
d="M771.9 191c27.7 0 50.1 26.5 50.1 59.3v186.4l-100.2.3V250.3c0-32.8 22.4-59.3 50.1-59.3z"
|
||||
style={{
|
||||
fill: iconStart,
|
||||
}}
|
||||
/>
|
||||
<linearGradient
|
||||
id="homepage_favicon_gradient"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1={200.746}
|
||||
y1={225.015}
|
||||
x2={764.986}
|
||||
y2={789.255}
|
||||
>
|
||||
<stop
|
||||
offset={0}
|
||||
style={{
|
||||
stopColor: iconStart,
|
||||
}}
|
||||
/>
|
||||
<stop
|
||||
offset={1}
|
||||
style={{
|
||||
stopColor: iconEnd,
|
||||
}}
|
||||
/>
|
||||
</linearGradient>
|
||||
<path
|
||||
d="M721.8 250.3c0-32.7 22.4-59.3 50.1-59.3H253.1c-27.7 0-50.1 26.5-50.1 59.3v582.2l90.2-75.7-.1-130.3H375v61.8l88-73.8 258.8 217.9V250.6"
|
||||
style={{
|
||||
fill: "url(#homepage_favicon_gradient})",
|
||||
}}
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Favicon() {
|
||||
const svgRef = useRef();
|
||||
const imgRef = useRef();
|
||||
const canvasRef = useRef();
|
||||
|
||||
useEffect(() => {
|
||||
const svg = svgRef.current;
|
||||
const img = imgRef.current;
|
||||
const canvas = canvasRef.current;
|
||||
|
||||
if (!svg || !img || !canvas) {
|
||||
return;
|
||||
}
|
||||
|
||||
const xml = new XMLSerializer().serializeToString(svg);
|
||||
|
||||
const svg64 = Buffer.from(xml).toString("base64");
|
||||
const b64Start = "data:image/svg+xml;base64,";
|
||||
|
||||
// prepend a "header"
|
||||
const image64 = b64Start + svg64;
|
||||
|
||||
// set it as the source of the img element
|
||||
img.onload = () => {
|
||||
// draw the image onto the canvas
|
||||
canvas.getContext("2d").drawImage(img, 0, 0);
|
||||
// canvas.width = 256;
|
||||
// canvas.height = 256;
|
||||
|
||||
const link = window.document.createElement("link");
|
||||
link.type = "image/x-icon";
|
||||
link.rel = "shortcut icon";
|
||||
link.href = canvas.toDataURL("image/x-icon");
|
||||
document.getElementsByTagName("head")[0].appendChild(link);
|
||||
};
|
||||
|
||||
img.src = image64;
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="hidden">
|
||||
<Svg svgRef={svgRef} />
|
||||
<img width={64} height={64} ref={imgRef} />
|
||||
<canvas width={64} height={64} ref={canvasRef} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,98 +1,132 @@
|
||||
import Image from "next/future/image";
|
||||
import { useContext } from "react";
|
||||
import { Disclosure } from "@headlessui/react";
|
||||
import classNames from "classnames";
|
||||
import { useContext, useState } from "react";
|
||||
|
||||
import Status from "./status";
|
||||
import Widget from "./widget";
|
||||
import Docker from "./widgets/service/docker";
|
||||
|
||||
import { SettingsContext } from "utils/settings-context";
|
||||
import Docker from "widgets/docker/component";
|
||||
import { SettingsContext } from "utils/contexts/settings";
|
||||
|
||||
function resolveIcon(icon) {
|
||||
if (icon.startsWith("http")) {
|
||||
return `/api/proxy?url=${encodeURIComponent(icon)}`;
|
||||
// direct or relative URLs
|
||||
if (icon.startsWith("http") || icon.startsWith("/")) {
|
||||
return <Image src={`${icon}`} width={32} height={32} alt="logo" />;
|
||||
}
|
||||
|
||||
if (icon.startsWith("/")) {
|
||||
return icon;
|
||||
// mdi- prefixed, material design icons
|
||||
if (icon.startsWith("mdi-")) {
|
||||
const iconName = icon.replace("mdi-", "").replace(".svg", "");
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
width: 32,
|
||||
height: 32,
|
||||
background: "linear-gradient(180deg, rgb(var(--color-logo-start)), rgb(var(--color-logo-stop)))",
|
||||
mask: `url(https://cdn.jsdelivr.net/npm/@mdi/svg@latest/svg/${iconName}.svg) no-repeat center / contain`,
|
||||
WebkitMask: `url(https://cdn.jsdelivr.net/npm/@mdi/svg@latest/svg/${iconName}.svg) no-repeat center / contain`,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (icon.endsWith(".png")) {
|
||||
return `https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/${icon}`;
|
||||
}
|
||||
|
||||
return `https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/${icon}.png`;
|
||||
// fallback to dashboard-icons
|
||||
const iconName = icon.replace(".png", "");
|
||||
return (
|
||||
<Image
|
||||
src={`https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/${iconName}.png`}
|
||||
width={32}
|
||||
height={32}
|
||||
alt="logo"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Item({ service }) {
|
||||
const hasLink = service.href && service.href !== "#";
|
||||
const { settings } = useContext(SettingsContext);
|
||||
const [statsOpen, setStatsOpen] = useState(false);
|
||||
const [statsClosing, setStatsClosing] = useState(false);
|
||||
|
||||
// set stats to closed after 300ms
|
||||
const closeStats = () => {
|
||||
if (statsOpen) {
|
||||
setStatsClosing(true);
|
||||
setTimeout(() => {
|
||||
setStatsOpen(false);
|
||||
setStatsClosing(false);
|
||||
}, 300);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<li key={service.name}>
|
||||
<Disclosure>
|
||||
<div
|
||||
className={`${
|
||||
hasLink ? "cursor-pointer " : " "
|
||||
}transition-all h-15 mb-3 p-1 rounded-md font-medium text-theme-700 hover:text-theme-700/70 dark:text-theme-200 dark:hover:text-theme-300 shadow-md shadow-black/10 dark:shadow-black/20 bg-white/50 hover:bg-theme-300/20 dark:bg-white/10 dark:hover:bg-white/20`}
|
||||
>
|
||||
<div className="flex select-none">
|
||||
{service.icon &&
|
||||
(hasLink ? (
|
||||
<a
|
||||
href={service.href}
|
||||
target={settings.target ?? "_blank"}
|
||||
rel="noreferrer"
|
||||
className="flex-shrink-0 flex items-center justify-center w-12 "
|
||||
>
|
||||
<Image src={resolveIcon(service.icon)} width={32} height={32} alt="logo" />
|
||||
</a>
|
||||
) : (
|
||||
<div className="flex-shrink-0 flex items-center justify-center w-12 ">
|
||||
<Image src={resolveIcon(service.icon)} width={32} height={32} alt="logo" />
|
||||
</div>
|
||||
))}
|
||||
|
||||
{hasLink ? (
|
||||
<div
|
||||
className={`${
|
||||
hasLink ? "cursor-pointer " : " "
|
||||
}transition-all h-15 mb-3 p-1 rounded-md font-medium text-theme-700 dark:text-theme-200 dark:hover:text-theme-300 shadow-md shadow-theme-900/10 dark:shadow-theme-900/20 bg-theme-100/20 hover:bg-theme-300/20 dark:bg-white/5 dark:hover:bg-white/10`}
|
||||
>
|
||||
<div className="flex select-none">
|
||||
{service.icon &&
|
||||
(hasLink ? (
|
||||
<a
|
||||
href={service.href}
|
||||
target={settings.target ?? "_blank"}
|
||||
rel="noreferrer"
|
||||
className="flex-1 flex items-center justify-between rounded-r-md "
|
||||
className="flex-shrink-0 flex items-center justify-center w-12 "
|
||||
>
|
||||
<div className="flex-1 px-2 py-2 text-sm text-left">
|
||||
{service.name}
|
||||
<p className="text-theme-500 dark:text-theme-400 text-xs font-extralight">{service.description}</p>
|
||||
</div>
|
||||
{resolveIcon(service.icon)}
|
||||
</a>
|
||||
) : (
|
||||
<div className="flex-1 flex items-center justify-between rounded-r-md ">
|
||||
<div className="flex-1 px-2 py-2 text-sm text-left">
|
||||
{service.name}
|
||||
<p className="text-theme-500 dark:text-theme-400 text-xs font-extralight">{service.description}</p>
|
||||
</div>
|
||||
<div className="flex-shrink-0 flex items-center justify-center w-12 ">{resolveIcon(service.icon)}</div>
|
||||
))}
|
||||
|
||||
{hasLink ? (
|
||||
<a
|
||||
href={service.href}
|
||||
target={settings.target ?? "_blank"}
|
||||
rel="noreferrer"
|
||||
className="flex-1 flex items-center justify-between rounded-r-md "
|
||||
>
|
||||
<div className="flex-1 px-2 py-2 text-sm text-left">
|
||||
{service.name}
|
||||
<p className="text-theme-500 dark:text-theme-300 text-xs font-light">{service.description}</p>
|
||||
</div>
|
||||
</a>
|
||||
) : (
|
||||
<div className="flex-1 flex items-center justify-between rounded-r-md ">
|
||||
<div className="flex-1 px-2 py-2 text-sm text-left">
|
||||
{service.name}
|
||||
<p className="text-theme-500 dark:text-theme-300 text-xs font-light">{service.description}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{service.container && (
|
||||
<Disclosure.Button
|
||||
as="div"
|
||||
className="flex-shrink-0 flex items-center justify-center w-12 cursor-pointer"
|
||||
>
|
||||
<Status service={service} />
|
||||
</Disclosure.Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Disclosure.Panel>
|
||||
<div className="w-full">
|
||||
<Docker service={{ widget: { container: service.container, server: service.server } }} />
|
||||
</div>
|
||||
</Disclosure.Panel>
|
||||
)}
|
||||
|
||||
{service.widget && <Widget service={service} />}
|
||||
{service.container && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => (statsOpen ? closeStats() : setStatsOpen(true))}
|
||||
className="flex-shrink-0 flex items-center justify-center w-12 cursor-pointer"
|
||||
>
|
||||
<Status service={service} />
|
||||
<span className="sr-only">View container stats</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</Disclosure>
|
||||
|
||||
{service.container && service.server && (
|
||||
<div
|
||||
className={classNames(
|
||||
statsOpen && !statsClosing ? "max-h-[55px] opacity-100" : " max-h-[0] opacity-0",
|
||||
"w-full overflow-hidden transition-all duration-300 ease-in-out"
|
||||
)}
|
||||
>
|
||||
{statsOpen && <Docker service={{ widget: { container: service.container, server: service.server } }} />}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{service.widget && <Widget service={service} />}
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,75 +1,19 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useTranslation } from "next-i18next";
|
||||
|
||||
const Sonarr = dynamic(() => import("./widgets/service/sonarr"));
|
||||
const Radarr = dynamic(() => import("./widgets/service/radarr"));
|
||||
const Lidarr = dynamic(() => import("./widgets/service/lidarr"));
|
||||
const Readarr = dynamic(() => import("./widgets/service/readarr"));
|
||||
const Bazarr = dynamic(() => import("./widgets/service/bazarr"));
|
||||
const Ombi = dynamic(() => import("./widgets/service/ombi"));
|
||||
const Portainer = dynamic(() => import("./widgets/service/portainer"));
|
||||
const Emby = dynamic(() => import("./widgets/service/emby"));
|
||||
const Nzbget = dynamic(() => import("./widgets/service/nzbget"));
|
||||
const SABnzbd = dynamic(() => import("./widgets/service/sabnzbd"));
|
||||
const Transmission = dynamic(() => import("./widgets/service/transmission"));
|
||||
const QBittorrent = dynamic(() => import("./widgets/service/qbittorrent"));
|
||||
const Docker = dynamic(() => import("./widgets/service/docker"));
|
||||
const Pihole = dynamic(() => import("./widgets/service/pihole"));
|
||||
const Rutorrent = dynamic(() => import("./widgets/service/rutorrent"));
|
||||
const Jellyfin = dynamic(() => import("./widgets/service/jellyfin"));
|
||||
const Speedtest = dynamic(() => import("./widgets/service/speedtest"));
|
||||
const Traefik = dynamic(() => import("./widgets/service/traefik"));
|
||||
const Jellyseerr = dynamic(() => import("./widgets/service/jellyseerr"));
|
||||
const Overseerr = dynamic(() => import("./widgets/service/overseerr"));
|
||||
const Npm = dynamic(() => import("./widgets/service/npm"));
|
||||
const Tautulli = dynamic(() => import("./widgets/service/tautulli"));
|
||||
const CoinMarketCap = dynamic(() => import("./widgets/service/coinmarketcap"));
|
||||
const Gotify = dynamic(() => import("./widgets/service/gotify"));
|
||||
const Prowlarr = dynamic(() => import("./widgets/service/prowlarr"));
|
||||
const Jackett = dynamic(() => import("./widgets/service/jackett"));
|
||||
const AdGuard = dynamic(() => import("./widgets/service/adguard"));
|
||||
const StRelaySrv = dynamic(() => import("./widgets/service/strelaysrv"));
|
||||
const Mastodon = dynamic(() => import("./widgets/service/mastodon"));
|
||||
|
||||
const widgetMappings = {
|
||||
docker: Docker,
|
||||
sonarr: Sonarr,
|
||||
radarr: Radarr,
|
||||
lidarr: Lidarr,
|
||||
readarr: Readarr,
|
||||
bazarr: Bazarr,
|
||||
ombi: Ombi,
|
||||
portainer: Portainer,
|
||||
emby: Emby,
|
||||
jellyfin: Jellyfin,
|
||||
nzbget: Nzbget,
|
||||
sabnzbd: SABnzbd,
|
||||
transmission: Transmission,
|
||||
qbittorrent: QBittorrent,
|
||||
pihole: Pihole,
|
||||
rutorrent: Rutorrent,
|
||||
speedtest: Speedtest,
|
||||
traefik: Traefik,
|
||||
jellyseerr: Jellyseerr,
|
||||
overseerr: Overseerr,
|
||||
coinmarketcap: CoinMarketCap,
|
||||
npm: Npm,
|
||||
tautulli: Tautulli,
|
||||
gotify: Gotify,
|
||||
prowlarr: Prowlarr,
|
||||
jackett: Jackett,
|
||||
adguard: AdGuard,
|
||||
strelaysrv: StRelaySrv,
|
||||
mastodon: Mastodon,
|
||||
};
|
||||
import ErrorBoundary from "components/errorboundry";
|
||||
import components from "widgets/components";
|
||||
|
||||
export default function Widget({ service }) {
|
||||
const { t } = useTranslation("common");
|
||||
|
||||
const ServiceWidget = widgetMappings[service.widget.type];
|
||||
const ServiceWidget = components[service.widget.type];
|
||||
|
||||
if (ServiceWidget) {
|
||||
return <ServiceWidget service={service} />;
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<ServiceWidget service={service} />
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
18
src/components/services/widget/block.jsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { useTranslation } from "next-i18next";
|
||||
import classNames from "classnames";
|
||||
|
||||
export default function Block({ value, label }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
"bg-theme-200/50 dark:bg-theme-900/20 rounded m-1 flex-1 flex flex-col items-center justify-center p-1",
|
||||
value === undefined ? "animate-pulse" : ""
|
||||
)}
|
||||
>
|
||||
<div className="font-thin text-sm">{value === undefined || value === null ? "-" : value}</div>
|
||||
<div className="font-bold text-xs uppercase">{t(label)}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
18
src/components/services/widget/container.jsx
Normal file
@@ -0,0 +1,18 @@
|
||||
export default function Container({ error = false, children, service }) {
|
||||
if (error) {
|
||||
return (
|
||||
<div className="bg-theme-200/50 dark:bg-theme-900/20 rounded m-1 flex-1 flex flex-col items-center justify-center p-1">
|
||||
<div className="font-thin text-sm">{error}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let visibleChildren = children;
|
||||
const fields = service?.widget?.fields;
|
||||
const type = service?.widget?.type;
|
||||
if (fields && type) {
|
||||
visibleChildren = children.filter(child => fields.some(field => `${type}.${field}` === child.props?.label));
|
||||
}
|
||||
|
||||
return <div className="relative flex flex-row w-full">{visibleChildren}</div>;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
export default function Block({ value, label }) {
|
||||
return (
|
||||
<div className="bg-theme-200/50 dark:bg-theme-900/20 rounded m-1 flex-1 flex flex-col items-center justify-center p-1">
|
||||
<div className="font-thin text-sm">{value === undefined || value === null ? "-" : value}</div>
|
||||
<div className="font-bold text-xs uppercase">{label}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Widget from "../widget";
|
||||
import Block from "../block";
|
||||
|
||||
import { formatApiUrl } from "utils/api-helpers";
|
||||
|
||||
export default function AdGuard({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const config = service.widget;
|
||||
|
||||
const { data: adguardData, error: adguardError } = useSWR(formatApiUrl(config, "stats"));
|
||||
|
||||
if (adguardError) {
|
||||
return <Widget error={t("widget.api_error")} />;
|
||||
}
|
||||
|
||||
if (!adguardData) {
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("adguard.queries")} />
|
||||
<Block label={t("adguard.blocked")} />
|
||||
<Block label={t("adguard.filtered")} />
|
||||
<Block label={t("adguard.latency")} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
const filtered =
|
||||
adguardData.num_replaced_safebrowsing + adguardData.num_replaced_safesearch + adguardData.num_replaced_parental;
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("adguard.queries")} value={t("common.number", { value: adguardData.num_dns_queries })} />
|
||||
<Block label={t("adguard.blocked")} value={t("common.number", { value: adguardData.num_blocked_filtering })} />
|
||||
<Block label={t("adguard.filtered")} value={t("common.number", { value: filtered })} />
|
||||
<Block
|
||||
label={t("adguard.latency")}
|
||||
value={t("common.ms", { value: adguardData.avg_processing_time * 1000, style: "unit", unit: "millisecond" })}
|
||||
/>
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Widget from "../widget";
|
||||
import Block from "../block";
|
||||
|
||||
import { formatApiUrl } from "utils/api-helpers";
|
||||
|
||||
export default function Bazarr({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const config = service.widget;
|
||||
|
||||
const { data: episodesData, error: episodesError } = useSWR(formatApiUrl(config, "episodes"));
|
||||
const { data: moviesData, error: moviesError } = useSWR(formatApiUrl(config, "movies"));
|
||||
|
||||
if (episodesError || moviesError) {
|
||||
return <Widget error={t("widget.api_error")} />;
|
||||
}
|
||||
|
||||
if (!episodesData || !moviesData) {
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("bazarr.missingEpisodes")} />
|
||||
<Block label={t("bazarr.missingMovies")} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("bazarr.missingEpisodes")} value={t("common.number", { value: episodesData.total })} />
|
||||
<Block label={t("bazarr.missingMovies")} value={t("common.number", { value: moviesData.total })} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Widget from "../widget";
|
||||
import Block from "../block";
|
||||
|
||||
import calculateCPUPercent from "utils/stats-helpers";
|
||||
|
||||
export default function Docker({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const config = service.widget;
|
||||
|
||||
const { data: statusData, error: statusError } = useSWR(
|
||||
`/api/docker/status/${config.container}/${config.server || ""}`,
|
||||
{
|
||||
refreshInterval: 5000,
|
||||
}
|
||||
);
|
||||
|
||||
const { data: statsData, error: statsError } = useSWR(
|
||||
`/api/docker/stats/${config.container}/${config.server || ""}`,
|
||||
{
|
||||
refreshInterval: 5000,
|
||||
}
|
||||
);
|
||||
|
||||
if (statsError || statusError) {
|
||||
return <Widget error={t("widget.api_error")} />;
|
||||
}
|
||||
|
||||
if (statusData && statusData.status !== "running") {
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("widget.status")} value={t("docker.offline")} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
if (!statsData || !statusData) {
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("docker.cpu")} />
|
||||
<Block label={t("docker.mem")} />
|
||||
<Block label={t("docker.rx")} />
|
||||
<Block label={t("docker.tx")} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("docker.cpu")} value={t("common.percent", { value: calculateCPUPercent(statsData.stats) })} />
|
||||
<Block label={t("docker.mem")} value={t("common.bytes", { value: statsData.stats.memory_stats.usage })} />
|
||||
{statsData.stats.networks && (
|
||||
<>
|
||||
<Block label={t("docker.rx")} value={t("common.bytes", { value: statsData.stats.networks.eth0.rx_bytes })} />
|
||||
<Block label={t("docker.tx")} value={t("common.bytes", { value: statsData.stats.networks.eth0.tx_bytes })} />
|
||||
</>
|
||||
)}
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Widget from "../widget";
|
||||
import Block from "../block";
|
||||
|
||||
import { formatApiUrl } from "utils/api-helpers";
|
||||
|
||||
export default function Gotify({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const config = service.widget;
|
||||
|
||||
const { data: appsData, error: appsError } = useSWR(formatApiUrl(config, `application`));
|
||||
const { data: messagesData, error: messagesError } = useSWR(formatApiUrl(config, `message`));
|
||||
const { data: clientsData, error: clientsError } = useSWR(formatApiUrl(config, `client`));
|
||||
|
||||
if (appsError || messagesError || clientsError) {
|
||||
return <Widget error={t("widget.api_error")} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("gotify.apps")} value={appsData?.length} />
|
||||
<Block label={t("gotify.clients")} value={clientsData?.length} />
|
||||
<Block label={t("gotify.messages")} value={messagesData?.messages?.length} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Widget from "../widget";
|
||||
import Block from "../block";
|
||||
|
||||
import { formatApiUrl } from "utils/api-helpers";
|
||||
|
||||
export default function Jackett({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const config = service.widget;
|
||||
|
||||
const { data: indexersData, error: indexersError } = useSWR(formatApiUrl(config, "indexers"));
|
||||
|
||||
if (indexersError) {
|
||||
return <Widget error={t("widget.api_error")} />;
|
||||
}
|
||||
|
||||
if (!indexersData) {
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("jackett.configured")} />
|
||||
<Block label={t("jackett.errored")} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
const errored = indexersData.filter((indexer) => indexer.last_error);
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("jackett.configured")} value={t("common.number", { value: indexersData.length })} />
|
||||
<Block label={t("jackett.errored")} value={t("common.number", { value: errored.length })} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import Emby from "./emby";
|
||||
|
||||
export default function Jellyfin({ service }) {
|
||||
return <Emby service={service} />;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Widget from "../widget";
|
||||
import Block from "../block";
|
||||
|
||||
import { formatApiUrl } from "utils/api-helpers";
|
||||
|
||||
export default function Jellyseerr({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const config = service.widget;
|
||||
|
||||
const { data: statsData, error: statsError } = useSWR(formatApiUrl(config, `request/count`));
|
||||
|
||||
if (statsError) {
|
||||
return <Widget error={t("widget.api_error")} />;
|
||||
}
|
||||
|
||||
if (!statsData) {
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("jellyseerr.pending")} />
|
||||
<Block label={t("jellyseerr.approved")} />
|
||||
<Block label={t("jellyseerr.available")} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("jellyseerr.pending")} value={statsData.pending} />
|
||||
<Block label={t("jellyseerr.approved")} value={statsData.approved} />
|
||||
<Block label={t("jellyseerr.available")} value={statsData.available} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Widget from "../widget";
|
||||
import Block from "../block";
|
||||
|
||||
import { formatApiUrl } from "utils/api-helpers";
|
||||
|
||||
export default function Lidarr({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const config = service.widget;
|
||||
|
||||
const { data: albumsData, error: albumsError } = useSWR(formatApiUrl(config, "album"));
|
||||
const { data: wantedData, error: wantedError } = useSWR(formatApiUrl(config, "wanted/missing"));
|
||||
const { data: queueData, error: queueError } = useSWR(formatApiUrl(config, "queue/status"));
|
||||
|
||||
if (albumsError || wantedError || queueError) {
|
||||
return <Widget error={t("widget.api_error")} />;
|
||||
}
|
||||
|
||||
if (!albumsData || !wantedData || !queueData) {
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("lidarr.wanted")} />
|
||||
<Block label={t("lidarr.queued")} />
|
||||
<Block label={t("lidarr.albums")} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("lidarr.wanted")} value={t("common.number", { value: wantedData.totalRecords })} />
|
||||
<Block label={t("lidarr.queued")} value={t("common.number", { value: queueData.totalCount })} />
|
||||
<Block label={t("lidarr.albums")} value={t("common.number", { value: albumsData.have })} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Widget from "../widget";
|
||||
import Block from "../block";
|
||||
|
||||
import { formatApiUrl } from "utils/api-helpers";
|
||||
|
||||
export default function Mastodon({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const config = service.widget;
|
||||
|
||||
const { data: statsData, error: statsError } = useSWR(formatApiUrl(config, `instance`));
|
||||
|
||||
if (statsError) {
|
||||
return <Widget error={t("widget.api_error")} />;
|
||||
}
|
||||
|
||||
if (!statsData) {
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("mastodon.user_count")} />
|
||||
<Block label={t("mastodon.status_count")} />
|
||||
<Block label={t("mastodon.domain_count")} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("mastodon.user_count")} value={t("common.number", { value: statsData.stats.user_count })} />
|
||||
<Block label={t("mastodon.status_count")} value={t("common.number", { value: statsData.stats.status_count })} />
|
||||
<Block label={t("mastodon.domain_count")} value={t("common.number", { value: statsData.stats.domain_count })} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Widget from "../widget";
|
||||
import Block from "../block";
|
||||
|
||||
import { formatApiUrl } from "utils/api-helpers";
|
||||
|
||||
export default function Npm({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const config = service.widget;
|
||||
|
||||
const { data: infoData, error: infoError } = useSWR(formatApiUrl(config, "nginx/proxy-hosts"));
|
||||
|
||||
if (infoError) {
|
||||
return <Widget error={t("widget.api_error")} />;
|
||||
}
|
||||
|
||||
if (!infoData) {
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("npm.enabled")} />
|
||||
<Block label={t("npm.disabled")} />
|
||||
<Block label={t("npm.total")} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
const enabled = infoData.filter((c) => c.enabled === 1).length;
|
||||
const disabled = infoData.filter((c) => c.enabled === 0).length;
|
||||
const total = infoData.length;
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("npm.enabled")} value={enabled} />
|
||||
<Block label={t("npm.disabled")} value={disabled} />
|
||||
<Block label={t("npm.total")} value={total} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Widget from "../widget";
|
||||
import Block from "../block";
|
||||
|
||||
import { formatApiUrl } from "utils/api-helpers";
|
||||
|
||||
export default function Nzbget({ service }) {
|
||||
const { t } = useTranslation("common");
|
||||
|
||||
const config = service.widget;
|
||||
|
||||
const { data: statusData, error: statusError } = useSWR(formatApiUrl(config, "status"));
|
||||
|
||||
if (statusError) {
|
||||
return <Widget error={t("widget.api_error")} />;
|
||||
}
|
||||
|
||||
if (!statusData) {
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("nzbget.rate")} />
|
||||
<Block label={t("nzbget.remaining")} />
|
||||
<Block label={t("nzbget.downloaded")} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("nzbget.rate")} value={t("common.bitrate", { value: statusData.DownloadRate })} />
|
||||
<Block
|
||||
label={t("nzbget.remaining")}
|
||||
value={t("common.bytes", { value: statusData.RemainingSizeMB * 1024 * 1024 })}
|
||||
/>
|
||||
<Block
|
||||
label={t("nzbget.downloaded")}
|
||||
value={t("common.bytes", { value: statusData.DownloadedSizeMB * 1024 * 1024 })}
|
||||
/>
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Widget from "../widget";
|
||||
import Block from "../block";
|
||||
|
||||
import { formatApiUrl } from "utils/api-helpers";
|
||||
|
||||
export default function Ombi({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const config = service.widget;
|
||||
|
||||
const { data: statsData, error: statsError } = useSWR(formatApiUrl(config, `Request/count`));
|
||||
|
||||
if (statsError) {
|
||||
return <Widget error={t("widget.api_error")} />;
|
||||
}
|
||||
|
||||
if (!statsData) {
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("ombi.pending")} />
|
||||
<Block label={t("ombi.approved")} />
|
||||
<Block label={t("ombi.available")} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("ombi.pending")} value={statsData.pending} />
|
||||
<Block label={t("ombi.approved")} value={statsData.approved} />
|
||||
<Block label={t("ombi.available")} value={statsData.available} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Widget from "../widget";
|
||||
import Block from "../block";
|
||||
|
||||
import { formatApiUrl } from "utils/api-helpers";
|
||||
|
||||
export default function Overseerr({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const config = service.widget;
|
||||
|
||||
const { data: statsData, error: statsError } = useSWR(formatApiUrl(config, `request/count`));
|
||||
|
||||
if (statsError) {
|
||||
return <Widget error={t("widget.api_error")} />;
|
||||
}
|
||||
|
||||
if (!statsData) {
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("overseerr.pending")} />
|
||||
<Block label={t("overseerr.approved")} />
|
||||
<Block label={t("overseerr.available")} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("overseerr.pending")} value={statsData.pending} />
|
||||
<Block label={t("overseerr.approved")} value={statsData.approved} />
|
||||
<Block label={t("overseerr.available")} value={statsData.available} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Widget from "../widget";
|
||||
import Block from "../block";
|
||||
|
||||
import { formatApiUrl } from "utils/api-helpers";
|
||||
|
||||
export default function Pihole({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const config = service.widget;
|
||||
|
||||
const { data: piholeData, error: piholeError } = useSWR(formatApiUrl(config, "api.php"));
|
||||
|
||||
if (piholeError) {
|
||||
return <Widget error={t("widget.api_error")} />;
|
||||
}
|
||||
|
||||
if (!piholeData) {
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("pihole.queries")} />
|
||||
<Block label={t("pihole.blocked")} />
|
||||
<Block label={t("pihole.gravity")} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("pihole.queries")} value={t("common.number", { value: piholeData.dns_queries_today })} />
|
||||
<Block label={t("pihole.blocked")} value={t("common.number", { value: piholeData.ads_blocked_today })} />
|
||||
<Block label={t("pihole.gravity")} value={t("common.number", { value: piholeData.domains_being_blocked })} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Widget from "../widget";
|
||||
import Block from "../block";
|
||||
|
||||
import { formatApiUrl } from "utils/api-helpers";
|
||||
|
||||
export default function Portainer({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const config = service.widget;
|
||||
|
||||
const { data: containersData, error: containersError } = useSWR(formatApiUrl(config, `docker/containers/json?all=1`));
|
||||
|
||||
if (containersError) {
|
||||
return <Widget error={t("widget.api_error")} />;
|
||||
}
|
||||
|
||||
if (!containersData) {
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("portainer.running")} />
|
||||
<Block label={t("portainer.stopped")} />
|
||||
<Block label={t("portainer.total")} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
if (containersData.error) {
|
||||
return <Widget error={t("widget.api_error")} />;
|
||||
}
|
||||
|
||||
const running = containersData.filter((c) => c.State === "running").length;
|
||||
const stopped = containersData.filter((c) => c.State === "exited").length;
|
||||
const total = containersData.length;
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("portainer.running")} value={running} />
|
||||
<Block label={t("portainer.stopped")} value={stopped} />
|
||||
<Block label={t("portainer.total")} value={total} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Widget from "../widget";
|
||||
import Block from "../block";
|
||||
|
||||
import { formatApiUrl } from "utils/api-helpers";
|
||||
|
||||
export default function Prowlarr({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const config = service.widget;
|
||||
|
||||
const { data: indexersData, error: indexersError } = useSWR(formatApiUrl(config, "indexer"));
|
||||
const { data: grabsData, error: grabsError } = useSWR(formatApiUrl(config, "indexerstats"));
|
||||
|
||||
if (indexersError || grabsError) {
|
||||
return <Widget error={t("widget.api_error")} />;
|
||||
}
|
||||
|
||||
if (!indexersData || !grabsData) {
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("prowlarr.enableIndexers")} />
|
||||
<Block label={t("prowlarr.numberOfGrabs")} />
|
||||
<Block label={t("prowlarr.numberOfQueries")} />
|
||||
<Block label={t("prowlarr.numberOfFailGrabs")} />
|
||||
<Block label={t("prowlarr.numberOfFailQueries")} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
const indexers = indexersData?.filter((indexer) => indexer.enable === true);
|
||||
|
||||
let numberOfGrabs = 0
|
||||
let numberOfQueries = 0
|
||||
let numberOfFailedGrabs = 0
|
||||
let numberOfFailedQueries = 0
|
||||
grabsData?.indexers?.forEach(element => {
|
||||
numberOfGrabs += element.numberOfGrabs;
|
||||
numberOfQueries += element.numberOfQueries;
|
||||
numberOfFailedGrabs += numberOfFailedGrabs + element.numberOfFailedGrabs;
|
||||
numberOfFailedQueries += numberOfFailedQueries + element.numberOfFailedQueries;
|
||||
});
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("prowlarr.enableIndexers")} value={indexers.length} />
|
||||
<Block label={t("prowlarr.numberOfGrabs")} value={numberOfGrabs} />
|
||||
<Block label={t("prowlarr.numberOfQueries")} value={numberOfQueries} />
|
||||
<Block label={t("prowlarr.numberOfFailGrabs")} value={numberOfFailedGrabs} />
|
||||
<Block label={t("prowlarr.numberOfFailQueries")} value={numberOfFailedQueries} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Widget from "../widget";
|
||||
import Block from "../block";
|
||||
|
||||
import { formatApiUrl } from "utils/api-helpers";
|
||||
|
||||
export default function QBittorrent ({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const config = service.widget;
|
||||
|
||||
const { data: torrentData, error: torrentError } = useSWR(formatApiUrl(config, "torrents/info"));
|
||||
|
||||
if (torrentError) {
|
||||
return <Widget error={t("widget.api_error")} />;
|
||||
}
|
||||
|
||||
if (!torrentData) {
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("qbittorrent.leech")} />
|
||||
<Block label={t("qbittorrent.download")} />
|
||||
<Block label={t("qbittorrent.seed")} />
|
||||
<Block label={t("qbittorrent.upload")} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
let rateDl = 0;
|
||||
let rateUl = 0;
|
||||
let completed = 0;
|
||||
|
||||
for (let i = 0; i < torrentData.length; i += 1) {
|
||||
const torrent = torrentData[i];
|
||||
rateDl += torrent.dlspeed;
|
||||
rateUl += torrent.upspeed;
|
||||
if (torrent.progress === 1) {
|
||||
completed += 1;
|
||||
}
|
||||
}
|
||||
|
||||
const leech = torrentData.length - completed;
|
||||
|
||||
let unitsDl = "KB/s";
|
||||
let unitsUl = "KB/s";
|
||||
rateDl /= 1024;
|
||||
rateUl /= 1024;
|
||||
|
||||
if (rateDl > 1024) {
|
||||
rateDl /= 1024;
|
||||
unitsDl = "MB/s";
|
||||
}
|
||||
|
||||
if (rateUl > 1024) {
|
||||
rateUl /= 1024;
|
||||
unitsUl = "MB/s";
|
||||
}
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("qbittorrent.leech")} value={t("common.number", { value: leech })} />
|
||||
<Block label={t("qbittorrent.download")} value={`${rateDl.toFixed(2)} ${unitsDl}`} />
|
||||
<Block label={t("qbittorrent.seed")} value={t("common.number", { value: completed })} />
|
||||
<Block label={t("qbittorrent.upload")} value={`${rateUl.toFixed(2)} ${unitsUl}`} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Widget from "../widget";
|
||||
import Block from "../block";
|
||||
|
||||
import { formatApiUrl } from "utils/api-helpers";
|
||||
|
||||
export default function Radarr({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const config = service.widget;
|
||||
|
||||
const { data: moviesData, error: moviesError } = useSWR(formatApiUrl(config, "movie"));
|
||||
const { data: queuedData, error: queuedError } = useSWR(formatApiUrl(config, "queue/status"));
|
||||
|
||||
if (moviesError || queuedError) {
|
||||
return <Widget error={t("widget.api_error")} />;
|
||||
}
|
||||
|
||||
if (!moviesData || !queuedData) {
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("radarr.wanted")} />
|
||||
<Block label={t("radarr.queued")} />
|
||||
<Block label={t("radarr.movies")} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("radarr.wanted")} value={moviesData.wanted} />
|
||||
<Block label={t("radarr.queued")} value={queuedData.totalCount} />
|
||||
<Block label={t("radarr.movies")} value={moviesData.have} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Widget from "../widget";
|
||||
import Block from "../block";
|
||||
|
||||
import { formatApiUrl } from "utils/api-helpers";
|
||||
|
||||
export default function Readarr({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const config = service.widget;
|
||||
|
||||
const { data: booksData, error: booksError } = useSWR(formatApiUrl(config, "book"));
|
||||
const { data: wantedData, error: wantedError } = useSWR(formatApiUrl(config, "wanted/missing"));
|
||||
const { data: queueData, error: queueError } = useSWR(formatApiUrl(config, "queue/status"));
|
||||
|
||||
if (booksError || wantedError || queueError) {
|
||||
return <Widget error={t("widget.api_error")} />;
|
||||
}
|
||||
|
||||
if (!booksData || !wantedData || !queueData) {
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("readarr.wanted")} />
|
||||
<Block label={t("readarr.queued")} />
|
||||
<Block label={t("readarr.books")} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("readarr.wanted")} value={t("common.number", { value: wantedData.totalRecords })} />
|
||||
<Block label={t("readarr.queued")} value={t("common.number", { value: queueData.totalCount })} />
|
||||
<Block label={t("readarr.books")} value={t("common.number", { value: booksData.have })} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Widget from "../widget";
|
||||
import Block from "../block";
|
||||
|
||||
import { formatApiUrl } from "utils/api-helpers";
|
||||
|
||||
export default function Rutorrent({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const config = service.widget;
|
||||
|
||||
const { data: statusData, error: statusError } = useSWR(formatApiUrl(config));
|
||||
|
||||
if (statusError) {
|
||||
return <Widget error={t("widget.api_error")} />;
|
||||
}
|
||||
|
||||
if (!statusData) {
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("rutorrent.active")} />
|
||||
<Block label={t("rutorrent.upload")} />
|
||||
<Block label={t("rutorrent.download")} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
const upload = statusData.reduce((acc, torrent) => acc + parseInt(torrent["d.get_up_rate"], 10), 0);
|
||||
|
||||
const download = statusData.reduce((acc, torrent) => acc + parseInt(torrent["d.get_down_rate"], 10), 0);
|
||||
|
||||
const active = statusData.filter((torrent) => torrent["d.get_state"] === "1");
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("rutorrent.active")} value={active.length} />
|
||||
<Block label={t("rutorrent.upload")} value={t("common.bitrate", { value: upload })} />
|
||||
<Block label={t("rutorrent.download")} value={t("common.bitrate", { value: download })} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Widget from "../widget";
|
||||
import Block from "../block";
|
||||
|
||||
import { formatApiUrl } from "utils/api-helpers";
|
||||
|
||||
export default function SABnzbd({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const config = service.widget;
|
||||
|
||||
const { data: queueData, error: queueError } = useSWR(formatApiUrl(config, "queue"));
|
||||
|
||||
if (queueError) {
|
||||
return <Widget error={t("widget.api_error")} />;
|
||||
}
|
||||
|
||||
if (!queueData) {
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("sabnzbd.rate")} />
|
||||
<Block label={t("sabnzbd.queue")} />
|
||||
<Block label={t("sabnzbd.timeleft")} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("sabnzbd.rate")} value={`${queueData.queue.speed}B/s`} />
|
||||
<Block label={t("sabnzbd.queue")} value={t("common.number", { value: queueData.queue.noofslots })} />
|
||||
<Block label={t("sabnzbd.timeleft")} value={queueData.queue.timeleft} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Widget from "../widget";
|
||||
import Block from "../block";
|
||||
|
||||
import { formatApiUrl } from "utils/api-helpers";
|
||||
|
||||
export default function Sonarr({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const config = service.widget;
|
||||
|
||||
const { data: wantedData, error: wantedError } = useSWR(formatApiUrl(config, "wanted/missing"));
|
||||
const { data: queuedData, error: queuedError } = useSWR(formatApiUrl(config, "queue"));
|
||||
const { data: seriesData, error: seriesError } = useSWR(formatApiUrl(config, "series"));
|
||||
|
||||
if (wantedError || queuedError || seriesError) {
|
||||
return <Widget error={t("widget.api_error")} />;
|
||||
}
|
||||
|
||||
if (!wantedData || !queuedData || !seriesData) {
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("sonarr.wanted")} />
|
||||
<Block label={t("sonarr.queued")} />
|
||||
<Block label={t("sonarr.series")} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("sonarr.wanted")} value={wantedData.totalRecords} />
|
||||
<Block label={t("sonarr.queued")} value={queuedData.totalRecords} />
|
||||
<Block label={t("sonarr.series")} value={seriesData.total} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Widget from "../widget";
|
||||
import Block from "../block";
|
||||
|
||||
import { formatApiUrl } from "utils/api-helpers";
|
||||
|
||||
export default function Speedtest({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const config = service.widget;
|
||||
|
||||
const { data: speedtestData, error: speedtestError } = useSWR(formatApiUrl(config, "speedtest/latest"));
|
||||
|
||||
if (speedtestError) {
|
||||
return <Widget error={t("widget.api_error")} />;
|
||||
}
|
||||
|
||||
if (!speedtestData) {
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("speedtest.download")} />
|
||||
<Block label={t("speedtest.upload")} />
|
||||
<Block label={t("speedtest.ping")} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
<Block
|
||||
label={t("speedtest.download")}
|
||||
value={t("common.bitrate", { value: speedtestData.data.download * 1024 * 1024 })}
|
||||
/>
|
||||
<Block
|
||||
label={t("speedtest.upload")}
|
||||
value={t("common.bitrate", { value: speedtestData.data.upload * 1024 * 1024 })}
|
||||
/>
|
||||
<Block
|
||||
label={t("speedtest.ping")}
|
||||
value={t("common.ms", { value: speedtestData.data.ping, style: "unit", unit: "millisecond" })}
|
||||
/>
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Widget from "../widget";
|
||||
import Block from "../block";
|
||||
|
||||
import { formatApiUrl } from "utils/api-helpers";
|
||||
|
||||
export default function StRelaySrv({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const config = service.widget;
|
||||
|
||||
const { data: statsData, error: statsError } = useSWR(formatApiUrl(config, `status`));
|
||||
|
||||
if (statsError) {
|
||||
return <Widget error={t("widget.api_error")} />;
|
||||
}
|
||||
|
||||
if (!statsData) {
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("strelaysrv.numActiveSessions")} />
|
||||
<Block label={t("strelaysrv.numConnections")} />
|
||||
<Block label={t("strelaysrv.bytesProxied")} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("strelaysrv.numActiveSessions")} value={t("common.number", { value: statsData.numActiveSessions })} />
|
||||
<Block label={t("strelaysrv.numConnections")} value={t("common.number", { value: statsData.numConnections })} />
|
||||
<Block label={t("strelaysrv.dataRelayed")} value={t("common.bytes", { value: statsData.bytesProxied })} />
|
||||
<Block label={t("strelaysrv.transferRate")} value={t("common.bitrate",{ value: statsData.kbps10s1m5m15m30m60m[5] })} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Widget from "../widget";
|
||||
import Block from "../block";
|
||||
|
||||
import { formatApiUrl } from "utils/api-helpers";
|
||||
|
||||
export default function Traefik({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const config = service.widget;
|
||||
|
||||
const { data: traefikData, error: traefikError } = useSWR(formatApiUrl(config, "overview"));
|
||||
|
||||
if (traefikError) {
|
||||
return <Widget error={t("widget.api_error")} />;
|
||||
}
|
||||
|
||||
if (!traefikData) {
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("traefik.routers")} />
|
||||
<Block label={t("traefik.services")} />
|
||||
<Block label={t("traefik.middleware")} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("traefik.routers")} value={traefikData.http.routers.total} />
|
||||
<Block label={t("traefik.services")} value={traefikData.http.services.total} />
|
||||
<Block label={t("traefik.middleware")} value={traefikData.http.middlewares.total} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Widget from "../widget";
|
||||
import Block from "../block";
|
||||
|
||||
import { formatApiUrl } from "utils/api-helpers";
|
||||
|
||||
export default function Transmission({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const config = service.widget;
|
||||
|
||||
const { data: torrentData, error: torrentError } = useSWR(formatApiUrl(config));
|
||||
|
||||
if (torrentError) {
|
||||
return <Widget error={t("widget.api_error")} />;
|
||||
}
|
||||
|
||||
if (!torrentData) {
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("transmission.leech")} />
|
||||
<Block label={t("transmission.download")} />
|
||||
<Block label={t("transmission.seed")} />
|
||||
<Block label={t("transmission.upload")} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
const { torrents } = torrentData.arguments;
|
||||
let rateDl = 0;
|
||||
let rateUl = 0;
|
||||
let completed = 0;
|
||||
|
||||
for (let i = 0; i < torrents.length; i += 1) {
|
||||
const torrent = torrents[i];
|
||||
rateDl += torrent.rateDownload;
|
||||
rateUl += torrent.rateUpload;
|
||||
if (torrent.percentDone === 1) {
|
||||
completed += 1;
|
||||
}
|
||||
}
|
||||
|
||||
const leech = torrents.length - completed;
|
||||
|
||||
let unitsDl = "KB/s";
|
||||
let unitsUl = "KB/s";
|
||||
rateDl /= 1024;
|
||||
rateUl /= 1024;
|
||||
|
||||
if (rateDl > 1024) {
|
||||
rateDl /= 1024;
|
||||
unitsDl = "MB/s";
|
||||
}
|
||||
|
||||
if (rateUl > 1024) {
|
||||
rateUl /= 1024;
|
||||
unitsUl = "MB/s";
|
||||
}
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
<Block label={t("transmission.leech")} value={t("common.number", { value: leech })} />
|
||||
<Block label={t("transmission.download")} value={`${rateDl.toFixed(2)} ${unitsDl}`} />
|
||||
<Block label={t("transmission.seed")} value={t("common.number", { value: completed })} />
|
||||
<Block label={t("transmission.upload")} value={`${rateUl.toFixed(2)} ${unitsUl}`} />
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
export default function Widget({ error = false, children }) {
|
||||
if (error) {
|
||||
return (
|
||||
<div className="bg-theme-200/50 dark:bg-theme-900/20 rounded m-1 flex-1 flex flex-col items-center justify-center p-1">
|
||||
<div className="font-thin text-sm">{error}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <div className="relative flex flex-row w-full">{children}</div>;
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import { IoColorPalette } from "react-icons/io5";
|
||||
import { Popover, Transition } from "@headlessui/react";
|
||||
import classNames from "classnames";
|
||||
|
||||
import { ColorContext } from "utils/color-context";
|
||||
import { ColorContext } from "utils/contexts/color";
|
||||
|
||||
const colors = [
|
||||
"slate",
|
||||
@@ -45,6 +45,7 @@ export default function ColorToggle() {
|
||||
className="h-5 w-5 text-theme-800 dark:text-theme-200 transition duration-150 ease-in-out"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span className="sr-only">Change color</span>
|
||||
</Popover.Button>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
@@ -67,6 +68,7 @@ export default function ColorToggle() {
|
||||
`rounded-md w-5 h-5 border-black/50 dark:border-white/50 theme-${color} bg-theme-400`
|
||||
)}
|
||||
/>
|
||||
<span className="sr-only">{color}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useContext } from "react";
|
||||
import { MdDarkMode, MdLightMode, MdToggleOff, MdToggleOn } from "react-icons/md";
|
||||
|
||||
import { ThemeContext } from "utils/theme-context";
|
||||
import { ThemeContext } from "utils/contexts/theme";
|
||||
|
||||
export default function ThemeToggle() {
|
||||
const { theme, setTheme } = useContext(ThemeContext);
|
||||
@@ -1,8 +1,10 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import useSWR from "swr";
|
||||
import { compareVersions } from "compare-versions";
|
||||
import { MdNewReleases } from "react-icons/md";
|
||||
|
||||
import cachedFetch from "utils/proxy/cached-fetch";
|
||||
|
||||
export default function Version() {
|
||||
const { t, i18n } = useTranslation();
|
||||
|
||||
@@ -10,7 +12,9 @@ export default function Version() {
|
||||
const revision = process.env.NEXT_PUBLIC_REVISION ?? "dev";
|
||||
const version = process.env.NEXT_PUBLIC_VERSION ?? "dev";
|
||||
|
||||
const { data: releaseData } = useSWR("https://api.github.com/repos/benphelps/homepage/releases");
|
||||
const cachedFetcher = (resource) => cachedFetch(resource, 5).then((res) => res.json());
|
||||
|
||||
const { data: releaseData } = useSWR("https://api.github.com/repos/benphelps/homepage/releases", cachedFetcher);
|
||||
|
||||
// use Intl.DateTimeFormat to format the date
|
||||
const formatDate = (date) => {
|
||||
@@ -26,10 +30,10 @@ export default function Version() {
|
||||
|
||||
return (
|
||||
<div className="flex flex-row items-center">
|
||||
<span className="text-xs text-theme-500 opacity-50">
|
||||
<span className="text-xs text-theme-500 dark:text-theme-400">
|
||||
{version} ({revision.substring(0, 7)}, {formatDate(buildTime)})
|
||||
</span>
|
||||
{version === "main" || version === "dev"
|
||||
{version === "main" || version === "dev" || version === "nightly"
|
||||
? null
|
||||
: releaseData &&
|
||||
compareVersions(latestRelease.tag_name, version) > 0 && (
|
||||
@@ -37,7 +41,7 @@ export default function Version() {
|
||||
href={latestRelease.html_url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="ml-2 text-xs text-theme-500 opacity-50 flex flex-row items-center"
|
||||
className="ml-2 text-xs text-theme-500 dark:text-theme-400 flex flex-row items-center"
|
||||
>
|
||||
<MdNewReleases className="mr-1" /> {t("Update Available")}
|
||||
</a>
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import WeatherApi from "components/widgets/weather/weather";
|
||||
import OpenWeatherMap from "components/widgets/openweathermap/weather";
|
||||
import Resources from "components/widgets/resources/resources";
|
||||
import Search from "components/widgets/search/search";
|
||||
import Greeting from "components/widgets/greeting/greeting";
|
||||
import DateTime from "components/widgets/datetime/datetime";
|
||||
|
||||
const widgetMappings = {
|
||||
weather: WeatherApi, // This key will be deprecated in the future
|
||||
weatherapi: WeatherApi,
|
||||
openweathermap: OpenWeatherMap,
|
||||
resources: Resources,
|
||||
search: Search,
|
||||
greeting: Greeting,
|
||||
datetime: DateTime,
|
||||
};
|
||||
|
||||
export default function Widget({ widget }) {
|
||||
const InfoWidget = widgetMappings[widget.type];
|
||||
|
||||
if (InfoWidget) {
|
||||
return <InfoWidget options={widget.options} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex-none flex flex-row items-center justify-center">
|
||||
Missing <strong>{widget.type}</strong>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useTranslation } from "next-i18next";
|
||||
|
||||
const textSizes = {
|
||||
"4xl": "text-4xl",
|
||||
|
||||
110
src/components/widgets/glances/glances.jsx
Normal file
@@ -0,0 +1,110 @@
|
||||
import useSWR from "swr";
|
||||
import { BiError } from "react-icons/bi";
|
||||
import { FaMemory } from "react-icons/fa";
|
||||
import { FiCpu } from "react-icons/fi";
|
||||
import { useTranslation } from "next-i18next";
|
||||
|
||||
import UsageBar from "../resources/usage-bar";
|
||||
|
||||
export default function Widget({ options }) {
|
||||
const { t, i18n } = useTranslation();
|
||||
|
||||
const { data, error } = useSWR(
|
||||
`/api/widgets/glances?${new URLSearchParams({ lang: i18n.language, ...options }).toString()}`, {
|
||||
refreshInterval: 1500,
|
||||
}
|
||||
);
|
||||
|
||||
if (error || data?.error) {
|
||||
return (
|
||||
<div className="flex flex-col justify-center first:ml-0 ml-4">
|
||||
<div className="flex flex-row items-center justify-end">
|
||||
<div className="flex flex-row items-center">
|
||||
<BiError className="w-8 h-8 text-theme-800 dark:text-theme-200" />
|
||||
<div className="flex flex-col ml-3 text-left">
|
||||
<span className="text-theme-800 dark:text-theme-200 text-sm">{t("widget.api_error")}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
return (
|
||||
<div className="flex flex-col max-w:full sm:basis-auto self-center grow-0 flex-wrap ml-4">
|
||||
<div className="flex flex-row self-center flex-wrap justify-between">
|
||||
<div className="flex-none flex flex-row items-center mr-3 py-1.5">
|
||||
<FiCpu className="text-theme-800 dark:text-theme-200 w-5 h-5" />
|
||||
<div className="flex flex-col ml-3 text-left min-w-[85px]">
|
||||
<div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
|
||||
<div className="pl-0.5 text-xs">
|
||||
{t("glances.wait")}
|
||||
</div>
|
||||
</div>
|
||||
<UsageBar percent="0" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-none flex flex-row items-center mr-3 py-1.5">
|
||||
<FaMemory className="text-theme-800 dark:text-theme-200 w-5 h-5" />
|
||||
<div className="flex flex-col ml-3 text-left min-w-[85px]">
|
||||
<div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
|
||||
<div className="pl-0.5 text-xs">
|
||||
{t("glances.wait")}
|
||||
</div>
|
||||
</div>
|
||||
<UsageBar percent="0" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{options.label && (
|
||||
<div className="ml-6 pt-1 text-center text-theme-800 dark:text-theme-200 text-xs">{options.label}</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col max-w:full sm:basis-auto self-center grow-0 flex-wrap ml-4">
|
||||
<div className="flex flex-row self-center flex-wrap justify-between">
|
||||
<div className="flex-none flex flex-row items-center mr-3 py-1.5">
|
||||
<FiCpu className="text-theme-800 dark:text-theme-200 w-5 h-5" />
|
||||
<div className="flex flex-col ml-3 text-left min-w-[85px]">
|
||||
<div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
|
||||
<div className="pl-0.5">
|
||||
{t("common.number", {
|
||||
value: data.cpu,
|
||||
style: "unit",
|
||||
unit: "percent",
|
||||
maximumFractionDigits: 0,
|
||||
})}
|
||||
</div>
|
||||
<div className="pr-1">{t("glances.cpu")}</div>
|
||||
</div>
|
||||
<UsageBar percent={data.cpu} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-none flex flex-row items-center mr-3 py-1.5">
|
||||
<FaMemory className="text-theme-800 dark:text-theme-200 w-5 h-5" />
|
||||
<div className="flex flex-col ml-3 text-left min-w-[85px]">
|
||||
<div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
|
||||
<div className="pl-0.5">
|
||||
{t("common.number", {
|
||||
value: data.mem,
|
||||
style: "unit",
|
||||
unit: "percent",
|
||||
maximumFractionDigits: 0,
|
||||
})}
|
||||
</div>
|
||||
<div className="pr-1">{t("glances.mem")}</div>
|
||||
</div>
|
||||
<UsageBar percent={data.mem} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{options.label && (
|
||||
<div className="pt-1 text-center text-theme-800 dark:text-theme-200 text-xs">{options.label}</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
56
src/components/widgets/logo/logo.jsx
Normal file
@@ -0,0 +1,56 @@
|
||||
export default function Logo() {
|
||||
return (
|
||||
<div className="w-12 h-12 flex flex-row items-center align-middle mr-3 self-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 1024 1024"
|
||||
style={{
|
||||
enableBackground: "new 0 0 1024 1024",
|
||||
}}
|
||||
xmlSpace="preserve"
|
||||
className="w-full h-full"
|
||||
>
|
||||
<style>
|
||||
{
|
||||
".st0{display:none}.st3{stroke-linecap:square}.st3,.st4{fill:none;stroke:#fff;stroke-miterlimit:10}.st6{display:inline;fill:#333}.st7{fill:#fff}"
|
||||
}
|
||||
</style>
|
||||
<g id="Icon">
|
||||
<path
|
||||
d="M771.9 191c27.7 0 50.1 26.5 50.1 59.3v186.4l-100.2.3V250.3c0-32.8 22.4-59.3 50.1-59.3z"
|
||||
style={{
|
||||
fill: "rgba(var(--color-logo-start))",
|
||||
}}
|
||||
/>
|
||||
<linearGradient
|
||||
id="homepage_logo_gradient"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1={200.746}
|
||||
y1={225.015}
|
||||
x2={764.986}
|
||||
y2={789.255}
|
||||
>
|
||||
<stop
|
||||
offset={0}
|
||||
style={{
|
||||
stopColor: "rgba(var(--color-logo-start))",
|
||||
}}
|
||||
/>
|
||||
<stop
|
||||
offset={1}
|
||||
style={{
|
||||
stopColor: "rgba(var(--color-logo-stop))",
|
||||
}}
|
||||
/>
|
||||
</linearGradient>
|
||||
<path
|
||||
d="M721.8 250.3c0-32.7 22.4-59.3 50.1-59.3H253.1c-27.7 0-50.1 26.5-50.1 59.3v582.2l90.2-75.7-.1-130.3H375v61.8l88-73.8 258.8 217.9V250.6"
|
||||
style={{
|
||||
fill: "url(#homepage_logo_gradient)",
|
||||
}}
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import mapIcon from "utils/owm-condition-map";
|
||||
import mapIcon from "utils/weather/owm-condition-map";
|
||||
|
||||
export default function Icon({ condition, timeOfDay }) {
|
||||
const IconComponent = mapIcon(condition, timeOfDay);
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useState } from "react";
|
||||
import { BiError } from "react-icons/bi";
|
||||
import { WiCloudDown } from "react-icons/wi";
|
||||
import { MdLocationDisabled, MdLocationSearching } from "react-icons/md";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useTranslation } from "next-i18next";
|
||||
|
||||
import Icon from "./icon";
|
||||
|
||||
@@ -16,9 +16,9 @@ function Widget({ options }) {
|
||||
|
||||
if (error || data?.cod === 401 || data?.error) {
|
||||
return (
|
||||
<div className="flex flex-col justify-center first:ml-0 ml-4">
|
||||
<div className="flex flex-col justify-center first:ml-auto ml-4 mr-2">
|
||||
<div className="flex flex-row items-center justify-end">
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="hidden sm:flex flex-col items-center">
|
||||
<BiError className="w-8 h-8 text-theme-800 dark:text-theme-200" />
|
||||
<div className="flex flex-col ml-3 text-left">
|
||||
<span className="text-theme-800 dark:text-theme-200 text-sm">{t("widget.api_error")}</span>
|
||||
@@ -32,9 +32,9 @@ function Widget({ options }) {
|
||||
|
||||
if (!data) {
|
||||
return (
|
||||
<div className="flex flex-col justify-center first:ml-0 ml-4">
|
||||
<div className="flex flex-col justify-center first:ml-auto ml-4 mr-2">
|
||||
<div className="flex flex-row items-center justify-end">
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="hidden sm:flex flex-col items-center">
|
||||
<WiCloudDown className="w-8 h-8 text-theme-800 dark:text-theme-200" />
|
||||
</div>
|
||||
<div className="flex flex-col ml-3 text-left">
|
||||
@@ -49,9 +49,9 @@ function Widget({ options }) {
|
||||
const unit = options.units === "metric" ? "celsius" : "fahrenheit";
|
||||
|
||||
return (
|
||||
<div className="flex flex-col justify-center first:ml-0 ml-4">
|
||||
<div className="flex flex-col justify-center first:ml-auto ml-2 mr-2">
|
||||
<div className="flex flex-row items-center justify-end">
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="hidden sm:flex flex-col items-center">
|
||||
<Icon
|
||||
condition={data.weather[0].id}
|
||||
timeOfDay={data.dt > data.sys.sunrise && data.dt < data.sys.sundown ? "day" : "night"}
|
||||
@@ -80,29 +80,35 @@ export default function OpenWeatherMap({ options }) {
|
||||
|
||||
const requestLocation = () => {
|
||||
setRequesting(true);
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(position) => {
|
||||
setLocation({ latitude: position.coords.latitude, longitude: position.coords.longitude });
|
||||
setRequesting(false);
|
||||
},
|
||||
() => {
|
||||
setRequesting(false);
|
||||
},
|
||||
{
|
||||
enableHighAccuracy: true,
|
||||
maximumAge: 1000 * 60 * 60 * 3,
|
||||
timeout: 1000 * 30,
|
||||
}
|
||||
);
|
||||
if (typeof window !== "undefined") {
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(position) => {
|
||||
setLocation({ latitude: position.coords.latitude, longitude: position.coords.longitude });
|
||||
setRequesting(false);
|
||||
},
|
||||
() => {
|
||||
setRequesting(false);
|
||||
},
|
||||
{
|
||||
enableHighAccuracy: true,
|
||||
maximumAge: 1000 * 60 * 60 * 3,
|
||||
timeout: 1000 * 30,
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
if (!requesting && !location) requestLocation();
|
||||
// if (!requesting && !location) requestLocation();
|
||||
|
||||
if (!location) {
|
||||
return (
|
||||
<button type="button" onClick={() => requestLocation()} className="flex flex-col justify-center first:ml-0 ml-4">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => requestLocation()}
|
||||
className="flex flex-col justify-center first:ml-auto ml-4 mr-2"
|
||||
>
|
||||
<div className="flex flex-row items-center justify-end">
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="hidden sm:flex flex-col items-center">
|
||||
{requesting ? (
|
||||
<MdLocationSearching className="w-6 h-6 text-theme-800 dark:text-theme-200 animate-pulse" />
|
||||
) : (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import useSWR from "swr";
|
||||
import { FiCpu } from "react-icons/fi";
|
||||
import { BiError } from "react-icons/bi";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useTranslation } from "next-i18next";
|
||||
|
||||
import UsageBar from "./usage-bar";
|
||||
|
||||
@@ -25,10 +25,20 @@ export default function Cpu({ expanded }) {
|
||||
|
||||
if (!data) {
|
||||
return (
|
||||
<div className="flex-none flex flex-row items-center mr-3 py-1.5">
|
||||
<div className="flex-none flex flex-row items-center mr-3 py-1.5 animate-pulse">
|
||||
<FiCpu className="text-theme-800 dark:text-theme-200 w-5 h-5" />
|
||||
<div className="flex flex-col ml-3 text-left">
|
||||
<span className="text-theme-800 dark:text-theme-200 text-xs">-</span>
|
||||
<div className="flex flex-col ml-3 text-left min-w-[85px]">
|
||||
<div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
|
||||
<div className="pl-0.5">-</div>
|
||||
<div className="pr-1">{t("resources.cpu")}</div>
|
||||
</div>
|
||||
{expanded && (
|
||||
<div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
|
||||
<div className="pl-0.5">-</div>
|
||||
<div className="pr-1">{t("resources.load")}</div>
|
||||
</div>
|
||||
)}
|
||||
<UsageBar percent={100} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -49,7 +59,7 @@ export default function Cpu({ expanded }) {
|
||||
maximumFractionDigits: 0,
|
||||
})}
|
||||
</div>
|
||||
<div className="pr-1">{t("docker.cpu")}</div>
|
||||
<div className="pr-1">{t("resources.cpu")}</div>
|
||||
</div>
|
||||
{expanded && (
|
||||
<div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
|
||||
|
||||