Compare commits

..

74 Commits

Author SHA1 Message Date
Henry Dollman
233349fb2a release 0.7.3 2024-11-04 21:33:19 -05:00
Henry Dollman
c54e6ff0ea po file formatting 2024-11-04 21:33:05 -05:00
Henry Dollman
98c4102f72 revert to previous behavior for displaying stopped containers
* Previously, a stopped container was completely removed from the chart/tooltip during the time period when it was not running. In the last few releases, the container remained in the chart with zero values if it was running at any time during the chart's duration. This restores the previous functionality.
2024-11-04 21:24:44 -05:00
hank
640ee7a88e New translations en.po (Polish) (#257) 2024-11-04 20:57:01 -05:00
Henry Dollman
8a85246a0b set lang in activateLocale func instead of dynamicActivate 2024-11-04 20:56:33 -05:00
Henry Dollman
655bfc95ca add ability to specify partition for extra disk using folder name 2024-11-04 20:52:27 -05:00
hank
37a066e6bd New Crowdin updates (#256)
* New translations en.po (French)

* New translations en.po (Spanish)

* New translations en.po (Arabic)

* New translations en.po (German)

* New translations en.po (Japanese)

* New translations en.po (Korean)

* New translations en.po (Portuguese)

* New translations en.po (Russian)

* New translations en.po (Turkish)

* New translations en.po (Ukrainian)

* New translations en.po (Chinese Simplified)

* New translations en.po (Vietnamese)

* New translations en.po (Chinese Traditional, Hong Kong)

* New translations en.po (Italian)
2024-11-04 16:01:01 -05:00
Henry Dollman
9e959a6b7b update translations 2024-11-04 15:42:23 -05:00
Henry Dollman
2b6560b9e1 update translation messages on build
* add lingui extract to build script
* delete translation ts files and add to gitignore
2024-11-04 15:34:59 -05:00
hank
d8836d53bf New Crowdin updates (#252)
* New translations en.po (French)

* New translations en.po (Spanish)

* New translations en.po (Arabic)

* New translations en.po (German)

* New translations en.po (Japanese)

* New translations en.po (Korean)

* New translations en.po (Portuguese)

* New translations en.po (Russian)

* New translations en.po (Turkish)

* New translations en.po (Ukrainian)

* New translations en.po (Chinese Simplified)

* New translations en.po (Vietnamese)

* New translations en.po (Chinese Traditional, Hong Kong)

* New translations en.po (Italian)
2024-11-04 14:30:27 -05:00
Henry Dollman
aa15876aa2 fix: read/write labels swapped for extra disk charts (#254) 2024-11-04 14:29:10 -05:00
Henry Dollman
7ca960b521 update system view grid to min xl 2024-11-04 14:18:08 -05:00
Henry Dollman
4eaedcf825 release 0.7.2 2024-11-03 15:31:39 -05:00
Henry Dollman
b337ba1d7f fix subheading for memory chart 2024-11-03 15:30:35 -05:00
hank
c9b72f724f New translations en.po (Ukrainian) (#251)
Co-authored-by: stanol <stanol777@gmail.com>
2024-11-03 15:02:59 -05:00
Henry Dollman
35d8996e00 release 0.7.1 2024-11-03 12:10:53 -05:00
Henry Dollman
6e61c5f1e4 update go deps 2024-11-03 12:10:17 -05:00
Henry Dollman
6bb147c349 fix en fallback if detected user locale is missing (#247) 2024-11-03 11:59:49 -05:00
hank
3668aa4e8e New Crowdin updates (#246)
* New translations en.po (French)

* New translations en.po (Spanish)

* New translations en.po (Arabic)

* New translations en.po (German)

* New translations en.po (Japanese)

* New translations en.po (Korean)

* New translations en.po (Portuguese)

* New translations en.po (Russian)

* New translations en.po (Turkish)

* New translations en.po (Ukrainian)

* New translations en.po (Chinese Simplified)

* New translations en.po (Vietnamese)

* New translations en.po (Chinese Traditional, Hong Kong)

* New translations en.po (Italian)

* New translations en.po (Ukrainian)
2024-11-03 11:28:27 -05:00
Henry Dollman
4c324bff73 release 0.7.0 2024-11-02 14:50:59 -04:00
Henry Dollman
741575df15 revert tweaks for old docker. needs more testing. 2024-11-02 14:43:35 -04:00
Henry Dollman
055fc39305 add italian and update other translations 2024-11-02 14:08:24 -04:00
Henry Dollman
5ae3a38204 Bandwidth alert max value increased to 1 Gigabit (closes #222) 2024-11-02 14:02:13 -04:00
Henry Dollman
44747e75b0 add columns filter for systems table 2024-11-02 13:35:14 -04:00
hank
e4f22ebb01 New Crowdin updates (#245)
* New translations en.po (French)

* New translations en.po (Spanish)

* New translations en.po (Arabic)

* New translations en.po (German)

* New translations en.po (Japanese)

* New translations en.po (Korean)

* New translations en.po (Portuguese)

* New translations en.po (Russian)

* New translations en.po (Turkish)

* New translations en.po (Ukrainian)

* New translations en.po (Chinese Simplified)

* New translations en.po (Vietnamese)

* New translations en.po (Chinese Traditional, Hong Kong)
2024-11-02 02:47:16 -04:00
Henry Dollman
bfb848a1ec add crowdin links to readme 2024-11-01 22:23:46 -04:00
Henry Dollman
c16c7830a4 update translation files 2024-11-01 22:11:58 -04:00
Henry Dollman
8f383c9f5e update translations 2024-11-01 21:24:49 -04:00
hank
5b68556a9a Update Crowdin configuration file 2024-11-01 20:49:15 -04:00
hank
cb1c481f54 Update Crowdin configuration file 2024-11-01 20:43:29 -04:00
Henry Dollman
a93ff63605 migrate to lingui 2024-11-01 20:31:57 -04:00
Henry Dollman
856683610a rtl layout updates 2024-10-31 22:15:21 -04:00
Henry Dollman
b9fda9dd0b update translations 2024-10-31 21:42:18 -04:00
Henry Dollman
7e27fee006 RTL layout fixes 2024-10-31 19:34:10 -04:00
Henry Dollman
f65d19ad84 add Turkish lang + style updates 2024-10-31 18:57:54 -04:00
Henry Dollman
94f771fc1c sort and format translation files 2024-10-31 17:50:05 -04:00
Weblate (bot)
0ac3d20162 Translations update from Hosted Weblate (#242)
* Translated using Weblate (Spanish)

Currently translated at 100.0% (165 of 165 strings)

Translation: Beszel/Hub web interface
Translate-URL: https://hosted.weblate.org/projects/beszel/hub-web-interface/es/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (165 of 165 strings)

Translation: Beszel/Hub web interface
Translate-URL: https://hosted.weblate.org/projects/beszel/hub-web-interface/uk/

---------

Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: stanol <stanol@users.noreply.hosted.weblate.org>
2024-10-31 17:34:16 -04:00
Henry Dollman
df0f3a154f rtl layout progress and updates to arabic translations 2024-10-31 16:48:28 -04:00
Henry Dollman
6419178d87 update readme 2024-10-30 20:49:15 -04:00
hank
91714ba0e6 weblate I18n (#238)
* update readme / weblate links

* Translated using Weblate (Arabic)

Currently translated at 96.3% (159 of 165 strings)

Translation: Beszel/Hub web interface
Translate-URL: https://hosted.weblate.org/projects/beszel/hub-web-interface/ar/

* Translated using Weblate (German)

Currently translated at 99.3% (164 of 165 strings)

Translation: Beszel/Hub web interface
Translate-URL: https://hosted.weblate.org/projects/beszel/hub-web-interface/de/

* Translated using Weblate (Spanish)

Currently translated at 99.3% (164 of 165 strings)

Translation: Beszel/Hub web interface
Translate-URL: https://hosted.weblate.org/projects/beszel/hub-web-interface/es/

* Translated using Weblate (French)

Currently translated at 99.3% (164 of 165 strings)

Translation: Beszel/Hub web interface
Translate-URL: https://hosted.weblate.org/projects/beszel/hub-web-interface/fr/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (165 of 165 strings)

Translation: Beszel/Hub web interface
Translate-URL: https://hosted.weblate.org/projects/beszel/hub-web-interface/ja/

* Translated using Weblate (Korean)

Currently translated at 100.0% (165 of 165 strings)

Translation: Beszel/Hub web interface
Translate-URL: https://hosted.weblate.org/projects/beszel/hub-web-interface/ko/

* Translated using Weblate (Portuguese)

Currently translated at 99.3% (164 of 165 strings)

Translation: Beszel/Hub web interface
Translate-URL: https://hosted.weblate.org/projects/beszel/hub-web-interface/pt/

* Translated using Weblate (Russian)

Currently translated at 99.3% (164 of 165 strings)

Translation: Beszel/Hub web interface
Translate-URL: https://hosted.weblate.org/projects/beszel/hub-web-interface/ru/

* Translated using Weblate (Ukrainian)

Currently translated at 99.3% (164 of 165 strings)

Translation: Beszel/Hub web interface
Translate-URL: https://hosted.weblate.org/projects/beszel/hub-web-interface/uk/

* Translated using Weblate (Vietnamese)

Currently translated at 100.0% (165 of 165 strings)

Translation: Beszel/Hub web interface
Translate-URL: https://hosted.weblate.org/projects/beszel/hub-web-interface/vi/

* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (165 of 165 strings)

Translation: Beszel/Hub web interface
Translate-URL: https://hosted.weblate.org/projects/beszel/hub-web-interface/zh_Hans/

* Translated using Weblate (Chinese (Traditional Han script, Hong Kong))

Currently translated at 99.3% (164 of 165 strings)

Translation: Beszel/Hub web interface
Translate-URL: https://hosted.weblate.org/projects/beszel/hub-web-interface/zh_Hant_HK/

---------

Co-authored-by: Anonymous <noreply@weblate.org>
2024-10-30 20:37:01 -04:00
Henry Dollman
b5ba5054a5 update readme / weblate links 2024-10-30 19:57:02 -04:00
Henry Dollman
6f38077ca0 add Ukrainian translations 2024-10-30 18:09:26 -04:00
Henry Dollman
7f82aafff9 add translations for chart tooltips 2024-10-30 17:53:44 -04:00
Henry Dollman
14a4715eb8 update translations 2024-10-30 16:06:57 -04:00
Henry Dollman
e4f1936698 translation updates 2024-10-30 14:16:04 -04:00
Henry Dollman
4f62a07da6 include english in main bundle (fixes fallback lang) 2024-10-30 13:53:42 -04:00
Henry Dollman
1a1fcebc46 spinner tweaks 2024-10-30 13:52:35 -04:00
Henry Dollman
f9f7db17d4 update translations + add Portuguese and Korean 2024-10-30 13:01:04 -04:00
Henry Dollman
929d94f705 update translations 2024-10-30 12:18:12 -04:00
Henry Dollman
2c4ea6f52a dynamically load translation files 2024-10-30 11:40:17 -04:00
Henry Dollman
3505b215a2 add prettier config and format files site files 2024-10-30 11:03:09 -04:00
Hank
8827996553 fix en/translation.json formatting 2024-10-30 01:20:16 -04:00
Henry Dollman
556a6b49db add Vietnamese, Japanese, and Arabic (need to check RTL) 2024-10-29 23:32:07 -04:00
ArsFy
180ec83a17 login i18n & chart loading & fix forgot pass page (#236)
Co-authored-by: hank <hank@henrygd.me>
2024-10-29 23:22:03 -04:00
Henry Dollman
062796b38c update navbar and home subtitle
* adds search button to navbar
* removes need for home.subtitle_2
2024-10-29 22:22:35 -04:00
Henry Dollman
67f88188e1 add makefile 2024-10-29 21:07:16 -04:00
Henry Dollman
3209c53201 add @esbuild/linux-arm64 as optional dependency 2024-10-29 20:08:54 -04:00
Henry Dollman
ec7aa80928 Merge branch 'ArsFy-main' 2024-10-29 18:09:29 -04:00
Henry Dollman
f6e391f8a9 i18n tweaks / layout fixes 2024-10-29 18:08:55 -04:00
Henry Dollman
e64fad9584 Merge branch 'main' of https://github.com/ArsFy/beszel into ArsFy-main 2024-10-29 15:47:07 -04:00
Bot_wxt1221
9e6ee8d239 fix(arm64/nixpkgs): add missing @esbuild for multi platform (#235) 2024-10-29 11:24:05 -04:00
Arsfy
2c66f93101 crowdin 2024-10-28 18:53:55 +08:00
Arsfy
5c2e2d7d36 i18n 2024-10-28 18:44:04 +08:00
Arsfy
376e8d4621 ctrl k & i18n 2024-10-28 13:37:21 +08:00
Henry Dollman
ec7cb53d93 update systemd install scripts to work if sudo not installed 2024-10-27 13:58:12 -04:00
Arsfy
b7176fc8f3 add copy linux install command 2024-10-27 14:30:34 +08:00
Henry Dollman
f8fc74116c rm *sensors.Warnings conversion - gopsutil windows uses different type 2024-10-26 14:02:19 -04:00
Henry Dollman
4094df3a61 fix: skip temperature collection if SENSORS is empty string (#196) 2024-10-24 15:10:20 -04:00
Henry Dollman
a5f9e2615c release 0.6.2 2024-10-23 18:39:15 -04:00
Henry Dollman
4a78ce1b16 skip temperatures code if sensors whitelist is set to empty string 2024-10-23 18:37:38 -04:00
Henry Dollman
f8f1e01cb4 add settings page and api route for generating config.yml 2024-10-23 18:30:24 -04:00
Henry Dollman
c7463f2b9f limit collection lookups and other small refactoring
* adds error handling for collection lookup (#216)
2024-10-23 13:10:39 -04:00
Henry Dollman
a975466fc7 add declarative system management with config.yml (#70, #206) 2024-10-22 18:46:52 -04:00
Henry Dollman
539c0ccb1d retry failed containers separately so we can run them in parallel (#58) 2024-10-21 17:00:13 -04:00
107 changed files with 19336 additions and 2866 deletions

4
.gitignore vendored
View File

@@ -11,3 +11,7 @@ dist
beszel/cmd/hub/hub
beszel/cmd/agent/agent
node_modules
beszel/build
*timestamp*
.swc
beszel/site/src/locales/**/*.ts

35
beszel/Makefile Normal file
View File

@@ -0,0 +1,35 @@
# Default OS/ARCH values
OS ?= $(shell go env GOOS)
ARCH ?= $(shell go env GOARCH)
# Skip building the web UI if true
SKIP_WEB ?= false
.PHONY: tidy build-agent build-hub build clean lint
.DEFAULT_GOAL := build
tidy:
go mod tidy
build-web-ui:
@if command -v bun >/dev/null 2>&1; then \
bun install --cwd ./site && \
bun run --cwd ./site build; \
else \
npm install --prefix ./site && \
npm run --prefix ./site build; \
fi
build-agent: tidy
CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) go build -o ./build/beszel-agent_$(OS)_$(ARCH) -ldflags "-w -s" beszel/cmd/agent
build-hub: tidy $(if $(filter false,$(SKIP_WEB)),build-web-ui)
CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) go build -o ./build/beszel_$(OS)_$(ARCH) -ldflags "-w -s" beszel/cmd/hub
build: build-agent build-hub
clean:
go clean
rm -rf ./build
lint:
golangci-lint run

View File

@@ -9,43 +9,44 @@ require (
github.com/goccy/go-json v0.10.3
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61
github.com/pocketbase/dbx v1.10.1
github.com/pocketbase/pocketbase v0.22.22
github.com/pocketbase/pocketbase v0.22.23
github.com/rhysd/go-github-selfupdate v1.2.3
github.com/shirou/gopsutil/v4 v4.24.9
github.com/shirou/gopsutil/v4 v4.24.10
github.com/spf13/cast v1.7.0
github.com/spf13/cobra v1.8.1
golang.org/x/crypto v0.28.0
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/AlecAivazis/survey/v2 v2.3.7 // indirect
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go-v2 v1.32.2 // indirect
github.com/aws/aws-sdk-go-v2 v1.32.3 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.6 // indirect
github.com/aws/aws-sdk-go-v2/config v1.28.0 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.41 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.17 // indirect
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.33 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.21 // indirect
github.com/aws/aws-sdk-go-v2/config v1.28.1 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.42 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.18 // indirect
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.35 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.22 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.2 // indirect
github.com/aws/aws-sdk-go-v2/service/s3 v1.66.0 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.24.2 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.32.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.3 // indirect
github.com/aws/aws-sdk-go-v2/service/s3 v1.66.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.24.3 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.3 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.32.3 // indirect
github.com/aws/smithy-go v1.22.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/disintegration/imaging v1.6.2 // indirect
github.com/domodwyer/mailyak/v3 v3.6.2 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/ebitengine/purego v0.8.0 // indirect
github.com/fatih/color v1.17.0 // indirect
github.com/ebitengine/purego v0.8.1 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.6 // indirect
github.com/ganigeorgiev/fexpr v0.4.1 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
@@ -72,7 +73,7 @@ require (
github.com/spf13/pflag v1.0.5 // indirect
github.com/tcnksm/go-gitconfig v0.1.2 // indirect
github.com/tklauser/go-sysconf v0.3.14 // indirect
github.com/tklauser/numcpus v0.8.0 // indirect
github.com/tklauser/numcpus v0.9.0 // indirect
github.com/ulikunitz/xz v0.5.12 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
@@ -89,8 +90,8 @@ require (
golang.org/x/text v0.19.0 // indirect
golang.org/x/time v0.7.0 // indirect
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
google.golang.org/api v0.201.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect
google.golang.org/api v0.204.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
google.golang.org/grpc v1.67.1 // indirect
google.golang.org/protobuf v1.35.1 // indirect
modernc.org/gc/v3 v3.0.0-20241004144649-1aea3fae8852 // indirect

View File

@@ -1,10 +1,10 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE=
cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U=
cloud.google.com/go/auth v0.9.8 h1:+CSJ0Gw9iVeSENVCKJoLHhdUykDgXSc4Qn+gu2BRtR8=
cloud.google.com/go/auth v0.9.8/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI=
cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY=
cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc=
cloud.google.com/go/auth v0.10.0 h1:tWlkvFAh+wwTOzXIjrwM64karR1iTBZ/GRr0S/DULYo=
cloud.google.com/go/auth v0.10.0/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI=
cloud.google.com/go/auth/oauth2adapt v0.2.5 h1:2p29+dePqsCHPP1bqDJcKj4qxRyYCcbzKpFyKGt3MTk=
cloud.google.com/go/auth/oauth2adapt v0.2.5/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8=
cloud.google.com/go/compute v1.14.0 h1:hfm2+FfxVmnRlh6LpB7cg1ZNU+5edAHmW679JePztk0=
cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo=
cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k=
@@ -26,42 +26,42 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3d
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/aws/aws-sdk-go-v2 v1.32.2 h1:AkNLZEyYMLnx/Q/mSKkcMqwNFXMAvFto9bNsHqcTduI=
github.com/aws/aws-sdk-go-v2 v1.32.2/go.mod h1:2SK5n0a2karNTv5tbP1SjsX0uhttou00v/HpXKM1ZUo=
github.com/aws/aws-sdk-go-v2 v1.32.3 h1:T0dRlFBKcdaUPGNtkBSwHZxrtis8CQU17UpNBZYd0wk=
github.com/aws/aws-sdk-go-v2 v1.32.3/go.mod h1:2SK5n0a2karNTv5tbP1SjsX0uhttou00v/HpXKM1ZUo=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.6 h1:pT3hpW0cOHRJx8Y0DfJUEQuqPild8jRGmSFmBgvydr0=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.6/go.mod h1:j/I2++U0xX+cr44QjHay4Cvxj6FUbnxrgmqN3H1jTZA=
github.com/aws/aws-sdk-go-v2/config v1.28.0 h1:FosVYWcqEtWNxHn8gB/Vs6jOlNwSoyOCA/g/sxyySOQ=
github.com/aws/aws-sdk-go-v2/config v1.28.0/go.mod h1:pYhbtvg1siOOg8h5an77rXle9tVG8T+BWLWAo7cOukc=
github.com/aws/aws-sdk-go-v2/credentials v1.17.41 h1:7gXo+Axmp+R4Z+AK8YFQO0ZV3L0gizGINCOWxSLY9W8=
github.com/aws/aws-sdk-go-v2/credentials v1.17.41/go.mod h1:u4Eb8d3394YLubphT4jLEwN1rLNq2wFOlT6OuxFwPzU=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.17 h1:TMH3f/SCAWdNtXXVPPu5D6wrr4G5hI1rAxbcocKfC7Q=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.17/go.mod h1:1ZRXLdTpzdJb9fwTMXiLipENRxkGMTn1sfKexGllQCw=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.33 h1:X+4YY5kZRI/cOoSMVMGTqFXHAMg1bvvay7IBcqHpybQ=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.33/go.mod h1:DPynzu+cn92k5UQ6tZhX+wfTB4ah6QDU/NgdHqatmvk=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.21 h1:UAsR3xA31QGf79WzpG/ixT9FZvQlh5HY1NRqSHBNOCk=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.21/go.mod h1:JNr43NFf5L9YaG3eKTm7HQzls9J+A9YYcGI5Quh1r2Y=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.21 h1:6jZVETqmYCadGFvrYEQfC5fAQmlo80CeL5psbno6r0s=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.21/go.mod h1:1SR0GbLlnN3QUmYaflZNiH1ql+1qrSiB2vwcJ+4UM60=
github.com/aws/aws-sdk-go-v2/config v1.28.1 h1:oxIvOUXy8x0U3fR//0eq+RdCKimWI900+SV+10xsCBw=
github.com/aws/aws-sdk-go-v2/config v1.28.1/go.mod h1:bRQcttQJiARbd5JZxw6wG0yIK3eLeSCPdg6uqmmlIiI=
github.com/aws/aws-sdk-go-v2/credentials v1.17.42 h1:sBP0RPjBU4neGpIYyx8mkU2QqLPl5u9cmdTWVzIpHkM=
github.com/aws/aws-sdk-go-v2/credentials v1.17.42/go.mod h1:FwZBfU530dJ26rv9saAbxa9Ej3eF/AK0OAY86k13n4M=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.18 h1:68jFVtt3NulEzojFesM/WVarlFpCaXLKaBxDpzkQ9OQ=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.18/go.mod h1:Fjnn5jQVIo6VyedMc0/EhPpfNlPl7dHV916O6B+49aE=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.35 h1:ihPPdcCVSN0IvBByXwqVp28/l4VosBZ6sDulcvU2J7w=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.35/go.mod h1:JkgEhs3SVF51Dj3m1Bj+yL8IznpxzkwlA3jLg3x7Kls=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22 h1:Jw50LwEkVjuVzE1NzkhNKkBf9cRN7MtE1F/b2cOKTUM=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22/go.mod h1:Y/SmAyPcOTmpeVaWSzSKiILfXTVJwrGmYZhcRbhWuEY=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22 h1:981MHwBaRZM7+9QSR6XamDzF/o7ouUGxFzr+nVSIhrs=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22/go.mod h1:1RA1+aBEfn+CAB/Mh0MB6LsdCYCnjZm7tKXtnk499ZQ=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.21 h1:7edmS3VOBDhK00b/MwGtGglCm7hhwNYnjJs/PgFdMQE=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.21/go.mod h1:Q9o5h4HoIWG8XfzxqiuK/CGUbepCJ8uTlaE3bAbxytQ=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.22 h1:yV+hCAHZZYJQcwAaszoBNwLbPItHvApxT0kVIw6jRgs=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.22/go.mod h1:kbR1TL8llqB1eGnVbybcA4/wgScxdylOdyAd51yxPdw=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 h1:TToQNkvGguu209puTojY/ozlqy2d/SFNcoLIqTFi42g=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0/go.mod h1:0jp+ltwkf+SwG2fm/PKo8t4y8pJSgOCO4D8Lz3k0aHQ=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.2 h1:4FMHqLfk0efmTqhXVRL5xYRqlEBNBiRI7N6w4jsEdd4=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.2/go.mod h1:LWoqeWlK9OZeJxsROW2RqrSPvQHKTpp69r/iDjwsSaw=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.2 h1:s7NA1SOw8q/5c0wr8477yOPp0z+uBaXBnLE0XYb0POA=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.2/go.mod h1:fnjjWyAW/Pj5HYOxl9LJqWtEwS7W2qgcRLWP+uWbss0=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.2 h1:t7iUP9+4wdc5lt3E41huP+GvQZJD38WLsgVp4iOtAjg=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.2/go.mod h1:/niFCtmuQNxqx9v8WAPq5qh7EH25U4BF6tjoyq9bObM=
github.com/aws/aws-sdk-go-v2/service/s3 v1.66.0 h1:xA6XhTF7PE89BCNHJbQi8VvPzcgMtmGC5dr8S8N7lHk=
github.com/aws/aws-sdk-go-v2/service/s3 v1.66.0/go.mod h1:cB6oAuus7YXRZhWCc1wIwPywwZ1XwweNp2TVAEGYeB8=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.2 h1:bSYXVyUzoTHoKalBmwaZxs97HU9DWWI3ehHSAMa7xOk=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.2/go.mod h1:skMqY7JElusiOUjMJMOv1jJsP7YUg7DrhgqZZWuzu1U=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.2 h1:AhmO1fHINP9vFYUE0LHzCWg/LfUWUF+zFPEcY9QXb7o=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.2/go.mod h1:o8aQygT2+MVP0NaV6kbdE1YnnIM8RRVQzoeUH45GOdI=
github.com/aws/aws-sdk-go-v2/service/sts v1.32.2 h1:CiS7i0+FUe+/YY1GvIBLLrR/XNGZ4CtM1Ll0XavNuVo=
github.com/aws/aws-sdk-go-v2/service/sts v1.32.2/go.mod h1:HtaiBI8CjYoNVde8arShXb94UbQQi9L4EMr6D+xGBwo=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.3 h1:kT6BcZsmMtNkP/iYMcRG+mIEA/IbeiUimXtGmqF39y0=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.3/go.mod h1:Z8uGua2k4PPaGOYn66pK02rhMrot3Xk3tpBuUFPomZU=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.3 h1:qcxX0JYlgWH3hpPUnd6U0ikcl6LLA9sLkXE2w1fpMvY=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.3/go.mod h1:cLSNEmI45soc+Ef8K/L+8sEA3A3pYFEYf5B5UI+6bH4=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.3 h1:ZC7Y/XgKUxwqcdhO5LE8P6oGP1eh6xlQReWNKfhvJno=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.3/go.mod h1:WqfO7M9l9yUAw0HcHaikwRd/H6gzYdz7vjejCA5e2oY=
github.com/aws/aws-sdk-go-v2/service/s3 v1.66.2 h1:p9TNFL8bFUMd+38YIpTAXpoxyz0MxC7FlbFEH4P4E1U=
github.com/aws/aws-sdk-go-v2/service/s3 v1.66.2/go.mod h1:fNjyo0Coen9QTwQLWeV6WO2Nytwiu+cCcWaTdKCAqqE=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.3 h1:UTpsIf0loCIWEbrqdLb+0RxnTXfWh2vhw4nQmFi4nPc=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.3/go.mod h1:FZ9j3PFHHAR+w0BSEjK955w5YD2UwB/l/H0yAK3MJvI=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.3 h1:2YCmIXv3tmiItw0LlYf6v7gEHebLY45kBEnPezbUKyU=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.3/go.mod h1:u19stRyNPxGhj6dRm+Cdgu6N75qnbW7+QN0q0dsAk58=
github.com/aws/aws-sdk-go-v2/service/sts v1.32.3 h1:wVnQ6tigGsRqSWDEEyH6lSAJ9OyFUsSnbaUWChuSGzs=
github.com/aws/aws-sdk-go-v2/service/sts v1.32.3/go.mod h1:VZa9yTFyj4o10YGsmDO4gbQJUvvhY72fhumT8W4LqsE=
github.com/aws/smithy-go v1.22.0 h1:uunKnWlcoL3zO7q+gG2Pk53joueEOsnNB28QdMsmiMM=
github.com/aws/smithy-go v1.22.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
@@ -84,14 +84,14 @@ github.com/domodwyer/mailyak/v3 v3.6.2 h1:x3tGMsyFhTCaxp6ycgR0FE/bu5QiNp+hetUuCO
github.com/domodwyer/mailyak/v3 v3.6.2/go.mod h1:lOm/u9CyCVWHeaAmHIdF4RiKVxKUT/H5XX10lIKAL6c=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/ebitengine/purego v0.8.0 h1:JbqvnEzRvPpxhCJzJJ2y0RbiZ8nyjccVUrSM3q+GvvE=
github.com/ebitengine/purego v0.8.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/ebitengine/purego v0.8.1 h1:sdRKd6plj7KYW33EH5As6YKfe8m9zbN9JMrOjNVF/BE=
github.com/ebitengine/purego v0.8.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
@@ -217,8 +217,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pocketbase/dbx v1.10.1 h1:cw+vsyfCJD8YObOVeqb93YErnlxwYMkNZ4rwN0G0AaA=
github.com/pocketbase/dbx v1.10.1/go.mod h1:xXRCIAKTHMgUCyCKZm55pUOdvFziJjQfXaWKhu2vhMs=
github.com/pocketbase/pocketbase v0.22.22 h1:iA128U+cmM9euxPpuCN7blmQ2FZNzOix2aUUcnbbQu8=
github.com/pocketbase/pocketbase v0.22.22/go.mod h1:u+l7T04g7eBXetoodXLch3WoV/QonRf1qYq+2vuTKuI=
github.com/pocketbase/pocketbase v0.22.23 h1:cnjSiBcMf7VIhXmoBmZCAV8qKYkOubHCOQQPZMKFBAk=
github.com/pocketbase/pocketbase v0.22.23/go.mod h1:h2ojT2pqBWH9LLl1aiawkwXiICKtzZA/kjM/8VhydR4=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@@ -229,8 +229,8 @@ github.com/rhysd/go-github-selfupdate v1.2.3/go.mod h1:mp/N8zj6jFfBQy/XMYoWsmfzx
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shirou/gopsutil/v4 v4.24.9 h1:KIV+/HaHD5ka5f570RZq+2SaeFsb/pq+fp2DGNWYoOI=
github.com/shirou/gopsutil/v4 v4.24.9/go.mod h1:3fkaHNeYsUFCGZ8+9vZVWtbyM1k2eRnlL+bWO8Bxa/Q=
github.com/shirou/gopsutil/v4 v4.24.10 h1:7VOzPtfw/5YDU+jLEoBwXwxJbQetULywoSV4RYY7HkM=
github.com/shirou/gopsutil/v4 v4.24.10/go.mod h1:s4D/wg+ag4rG0WO7AiTj2BeYCRhym0vM7DHbZRxnIT8=
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
@@ -251,8 +251,8 @@ github.com/tcnksm/go-gitconfig v0.1.2 h1:iiDhRitByXAEyjgBqsKi9QU4o2TNtv9kPP3RgPg
github.com/tcnksm/go-gitconfig v0.1.2/go.mod h1:/8EhP4H7oJZdIPyT+/UIsG87kTzrzM4UsLGSItWYCpE=
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY=
github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE=
github.com/tklauser/numcpus v0.9.0 h1:lmyCHtANi8aRUgkckBgoDk1nHCux3n2cgkJLXdQGPDo=
github.com/tklauser/numcpus v0.9.0/go.mod h1:SN6Nq1O3VychhC1npsWostA+oW+VOQTxZrS604NSRyI=
github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
@@ -364,8 +364,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
google.golang.org/api v0.201.0 h1:+7AD9JNM3tREtawRMu8sOjSbb8VYcYXJG/2eEOmfDu0=
google.golang.org/api v0.201.0/go.mod h1:HVY0FCHVs89xIW9fzf/pBvOEm+OolHa86G/txFezyq4=
google.golang.org/api v0.204.0 h1:3PjmQQEDkR/ENVZZwIYB4W/KzYtN8OrqnNcHWpeR8E4=
google.golang.org/api v0.204.0/go.mod h1:69y8QSoKIbL9F94bWgWAq6wGqGwyjBgi2y8rAK8zLag=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -373,12 +373,12 @@ google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCID
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20241007155032-5fefd90f89a9 h1:nFS3IivktIU5Mk6KQa+v6RKkHUpdQpphqGNLxqNnbEk=
google.golang.org/genproto v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:tEzYTYZxbmVNOu0OAFH9HzdJtLn6h4Aj89zzlBCdHms=
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 h1:Q3nlH8iSQSRUwOskjbcSMcF2jiYMNiQYZ0c2KEJLKKU=
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38/go.mod h1:xBI+tzfqGGN2JBeSebfKXFSdBpWVQ7sLW40PTupVRm4=
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 h1:wKguEg1hsxI2/L3hUYrpo1RVi48K+uTyzKqprwLXsb8=
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
@@ -398,6 +398,7 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=

View File

@@ -62,7 +62,9 @@ func (a *Agent) Run(pubKey []byte, addr string) {
if sensors, exists := os.LookupEnv("SENSORS"); exists {
a.sensorsWhitelist = make(map[string]struct{})
for _, sensor := range strings.Split(sensors, ",") {
a.sensorsWhitelist[sensor] = struct{}{}
if sensor != "" {
a.sensorsWhitelist[sensor] = struct{}{}
}
}
}

View File

@@ -41,11 +41,20 @@ func (a *Agent) initializeDiskInfo() {
if _, exists := a.fsStats[key]; !exists {
if root {
slog.Info("Detected root device", "name", key)
// check if root device is in /proc/diskstats, use fallback if not
// Check if root device is in /proc/diskstats, use fallback if not
if _, exists := diskIoCounters[key]; !exists {
slog.Warn("Device not found in diskstats", "name", key)
key = findFallbackIoDevice(filesystem, diskIoCounters, a.fsStats)
slog.Info("Using I/O fallback", "name", key)
slog.Info("Using I/O fallback", "device", device, "mountpoint", mountpoint, "fallback", key)
}
} else {
// Check if non-root has diskstats and fall back to folder name if not
// Scenario: device is encrypted and named luks-2bcb02be-999d-4417-8d18-5c61e660fb6e - not in /proc/diskstats.
// However, the device can be specified by mounting folder from luks device at /extra-filesystems/sda1
if _, exists := diskIoCounters[key]; !exists {
efBase := filepath.Base(mountpoint)
if _, exists := diskIoCounters[efBase]; exists {
key = efBase
}
}
}
a.fsStats[key] = &system.FsStats{Root: root, Mountpoint: mountpoint}
@@ -114,7 +123,7 @@ func (a *Agent) initializeDiskInfo() {
mountpoint := filepath.Join(efPath, folder.Name())
slog.Debug("/extra-filesystems", "mountpoint", mountpoint)
if !existingMountpoints[mountpoint] {
a.fsStats[folder.Name()] = &system.FsStats{Mountpoint: mountpoint}
addFsStat(folder.Name(), mountpoint, false)
}
}
}

View File

@@ -60,6 +60,8 @@ func (dm *dockerManager) getDockerStats() ([]*container.Stats, error) {
clear(dm.validIds)
}
var failedContainters []container.ApiInfo
for _, ctr := range *dm.apiContainerList {
ctr.IdShort = ctr.Id[:12]
dm.validIds[ctr.IdShort] = struct{}{}
@@ -74,18 +76,33 @@ func (dm *dockerManager) getDockerStats() ([]*container.Stats, error) {
defer dm.dequeue()
err := dm.updateContainerStats(ctr)
if err != nil {
dm.deleteContainerStatsSync(ctr.IdShort)
// retry once
err = dm.updateContainerStats(ctr)
if err != nil {
slog.Error("Error getting container stats", "err", err)
}
dm.containerStatsMutex.Lock()
delete(dm.containerStatsMap, ctr.IdShort)
failedContainters = append(failedContainters, ctr)
dm.containerStatsMutex.Unlock()
}
}()
}
dm.wg.Wait()
// retry failed containers separately so we can run them in parallel (docker 24 bug)
if len(failedContainters) > 0 {
slog.Debug("Retrying failed containers", "count", len(failedContainters))
// time.Sleep(time.Millisecond * 1100)
for _, ctr := range failedContainters {
dm.wg.Add(1)
go func() {
defer dm.wg.Done()
err = dm.updateContainerStats(ctr)
if err != nil {
slog.Error("Error getting container stats", "err", err)
}
}()
}
dm.wg.Wait()
}
// populate final stats and remove old / invalid container stats
stats := make([]*container.Stats, 0, containersLength)
for id, v := range dm.containerStatsMap {

View File

@@ -171,33 +171,36 @@ func (a *Agent) getSystemStats() system.Stats {
}
}
// temperatures
temps, err := sensors.TemperaturesWithContext(a.sensorsContext)
if err != nil {
// err.(*sensors.Warnings).Verbose = true
slog.Debug("Sensor error", "err", err)
}
slog.Debug("Temperature", "sensors", temps)
if len(temps) > 0 {
systemStats.Temperatures = make(map[string]float64, len(temps))
for i, sensor := range temps {
// skip if temperature is 0
if sensor.Temperature <= 0 || sensor.Temperature >= 200 {
continue
}
if _, ok := systemStats.Temperatures[sensor.SensorKey]; ok {
// if key already exists, append int to key
systemStats.Temperatures[sensor.SensorKey+"_"+strconv.Itoa(i)] = twoDecimals(sensor.Temperature)
} else {
systemStats.Temperatures[sensor.SensorKey] = twoDecimals(sensor.Temperature)
}
// temperatures (skip if sensors whitelist is set to empty string)
if a.sensorsWhitelist != nil && len(a.sensorsWhitelist) == 0 {
slog.Debug("Skipping temperature collection")
} else {
temps, err := sensors.TemperaturesWithContext(a.sensorsContext)
if err != nil {
slog.Debug("Sensor error", "err", err)
}
// remove sensors from systemStats if whitelist exists and sensor is not in whitelist
// (do this here instead of in initial loop so we have correct keys if int was appended)
if a.sensorsWhitelist != nil {
for key := range systemStats.Temperatures {
if _, nameInWhitelist := a.sensorsWhitelist[key]; !nameInWhitelist {
delete(systemStats.Temperatures, key)
slog.Debug("Temperature", "sensors", temps)
if len(temps) > 0 {
systemStats.Temperatures = make(map[string]float64, len(temps))
for i, sensor := range temps {
// skip if temperature is 0
if sensor.Temperature <= 0 || sensor.Temperature >= 200 {
continue
}
if _, ok := systemStats.Temperatures[sensor.SensorKey]; ok {
// if key already exists, append int to key
systemStats.Temperatures[sensor.SensorKey+"_"+strconv.Itoa(i)] = twoDecimals(sensor.Temperature)
} else {
systemStats.Temperatures[sensor.SensorKey] = twoDecimals(sensor.Temperature)
}
}
// remove sensors from systemStats if whitelist exists and sensor is not in whitelist
// (do this here instead of in initial loop so we have correct keys if int was appended)
if a.sensorsWhitelist != nil {
for key := range systemStats.Temperatures {
if _, nameInWhitelist := a.sensorsWhitelist[key]; !nameInWhitelist {
delete(systemStats.Temperatures, key)
}
}
}
}

View File

@@ -0,0 +1,222 @@
package hub
import (
"beszel/internal/entities/system"
"fmt"
"log"
"os"
"path/filepath"
"strconv"
"github.com/labstack/echo/v5"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/apis"
"github.com/pocketbase/pocketbase/models"
"github.com/spf13/cast"
"gopkg.in/yaml.v3"
)
type Config struct {
Systems []SystemConfig `yaml:"systems"`
}
type SystemConfig struct {
Name string `yaml:"name"`
Host string `yaml:"host"`
Port uint16 `yaml:"port"`
Users []string `yaml:"users"`
}
// Syncs systems with the config.yml file
func (h *Hub) syncSystemsWithConfig() error {
configPath := filepath.Join(h.app.DataDir(), "config.yml")
configData, err := os.ReadFile(configPath)
if err != nil {
return nil
}
var config Config
err = yaml.Unmarshal(configData, &config)
if err != nil {
return fmt.Errorf("failed to parse config.yml: %v", err)
}
if len(config.Systems) == 0 {
log.Println("No systems defined in config.yml.")
return nil
}
var firstUser *models.Record
// Create a map of email to user ID
userEmailToID := make(map[string]string)
users, err := h.app.Dao().FindRecordsByExpr("users", dbx.NewExp("id != ''"))
if err != nil {
return err
}
if len(users) > 0 {
firstUser = users[0]
for _, user := range users {
userEmailToID[user.GetString("email")] = user.Id
}
}
// add default settings for systems if not defined in config
for i := range config.Systems {
system := &config.Systems[i]
if system.Port == 0 {
system.Port = 45876
}
if len(users) > 0 && len(system.Users) == 0 {
// default to first user if none are defined
system.Users = []string{firstUser.Id}
} else {
// Convert email addresses to user IDs
userIDs := make([]string, 0, len(system.Users))
for _, email := range system.Users {
if id, ok := userEmailToID[email]; ok {
userIDs = append(userIDs, id)
} else {
log.Printf("User %s not found", email)
}
}
system.Users = userIDs
}
}
// Get existing systems
existingSystems, err := h.app.Dao().FindRecordsByExpr("systems", dbx.NewExp("id != ''"))
if err != nil {
return err
}
// Create a map of existing systems for easy lookup
existingSystemsMap := make(map[string]*models.Record)
for _, system := range existingSystems {
key := system.GetString("host") + ":" + system.GetString("port")
existingSystemsMap[key] = system
}
// Process systems from config
for _, sysConfig := range config.Systems {
key := sysConfig.Host + ":" + strconv.Itoa(int(sysConfig.Port))
if existingSystem, ok := existingSystemsMap[key]; ok {
// Update existing system
existingSystem.Set("name", sysConfig.Name)
existingSystem.Set("users", sysConfig.Users)
existingSystem.Set("port", sysConfig.Port)
if err := h.app.Dao().SaveRecord(existingSystem); err != nil {
return err
}
delete(existingSystemsMap, key)
} else {
// Create new system
systemsCollection, err := h.app.Dao().FindCollectionByNameOrId("systems")
if err != nil {
return fmt.Errorf("failed to find systems collection: %v", err)
}
newSystem := models.NewRecord(systemsCollection)
newSystem.Set("name", sysConfig.Name)
newSystem.Set("host", sysConfig.Host)
newSystem.Set("port", sysConfig.Port)
newSystem.Set("users", sysConfig.Users)
newSystem.Set("info", system.Info{})
newSystem.Set("status", "pending")
if err := h.app.Dao().SaveRecord(newSystem); err != nil {
return fmt.Errorf("failed to create new system: %v", err)
}
}
}
// Delete systems not in config
for _, system := range existingSystemsMap {
if err := h.app.Dao().DeleteRecord(system); err != nil {
return err
}
}
log.Println("Systems synced with config.yml")
return nil
}
// Generates content for the config.yml file as a YAML string
func (h *Hub) generateConfigYAML() (string, error) {
// Fetch all systems from the database
systems, err := h.app.Dao().FindRecordsByFilter("systems", "id != ''", "name", -1, 0)
if err != nil {
return "", err
}
// Create a Config struct to hold the data
config := Config{
Systems: make([]SystemConfig, 0, len(systems)),
}
// Fetch all users at once
allUserIDs := make([]string, 0)
for _, system := range systems {
allUserIDs = append(allUserIDs, system.GetStringSlice("users")...)
}
userEmailMap, err := h.getUserEmailMap(allUserIDs)
if err != nil {
return "", err
}
// Populate the Config struct with system data
for _, system := range systems {
userIDs := system.GetStringSlice("users")
userEmails := make([]string, 0, len(userIDs))
for _, userID := range userIDs {
if email, ok := userEmailMap[userID]; ok {
userEmails = append(userEmails, email)
}
}
sysConfig := SystemConfig{
Name: system.GetString("name"),
Host: system.GetString("host"),
Port: cast.ToUint16(system.Get("port")),
Users: userEmails,
}
config.Systems = append(config.Systems, sysConfig)
}
// Marshal the Config struct to YAML
yamlData, err := yaml.Marshal(&config)
if err != nil {
return "", err
}
// Add a header to the YAML
yamlData = append([]byte("# Values for port and users are optional.\n# Defaults are port 45876 and the first created user.\n\n"), yamlData...)
return string(yamlData), nil
}
// New helper function to get a map of user IDs to emails
func (h *Hub) getUserEmailMap(userIDs []string) (map[string]string, error) {
users, err := h.app.Dao().FindRecordsByIds("users", userIDs)
if err != nil {
return nil, err
}
userEmailMap := make(map[string]string, len(users))
for _, user := range users {
userEmailMap[user.Id] = user.GetString("email")
}
return userEmailMap, nil
}
// Returns the current config.yml file as a JSON object
func (h *Hub) getYamlConfig(c echo.Context) error {
requestData := apis.RequestInfo(c)
if requestData.AuthRecord == nil || requestData.AuthRecord.GetString("role") != "admin" {
return apis.NewForbiddenError("Forbidden", nil)
}
configContent, err := h.generateConfigYAML()
if err != nil {
return err
}
return c.JSON(200, map[string]string{"config": configContent})
}

View File

@@ -42,6 +42,8 @@ type Hub struct {
am *alerts.AlertManager
um *users.UserManager
rm *records.RecordManager
systemStats *models.Collection
containerStats *models.Collection
}
func NewHub(app *pocketbase.PocketBase) *Hub {
@@ -56,14 +58,10 @@ func NewHub(app *pocketbase.PocketBase) *Hub {
}
func (h *Hub) Run() {
// rm := records.NewRecordManager(h.app)
// am := alerts.NewAlertManager(h.app)
// um := users.NewUserManager(h.app)
// loosely check if it was executed using "go run"
isGoRun := strings.HasPrefix(os.Args[0], os.TempDir())
// // enable auto creation of migration files when making collection changes in the Admin UI
// enable auto creation of migration files when making collection changes in the Admin UI
migratecmd.MustRegister(h.app, h.app.RootCmd, migratecmd.Config{
// (the isGoRun check is to enable it only during development)
Automigrate: isGoRun,
@@ -93,7 +91,8 @@ func (h *Hub) Run() {
if err := h.app.Dao().SaveCollection(usersCollection); err != nil {
return err
}
return nil
// sync systems with config
return h.syncSystemsWithConfig()
})
// serve web ui
@@ -128,7 +127,11 @@ func (h *Hub) Run() {
// delete old records once every hour
scheduler.MustAdd("delete old records", "8 * * * *", h.rm.DeleteOldRecords)
// create longer records every 10 minutes
scheduler.MustAdd("create longer records", "*/10 * * * *", h.rm.CreateLongerRecords)
scheduler.MustAdd("create longer records", "*/10 * * * *", func() {
if systemStats, containerStats, err := h.getCollections(); err == nil {
h.rm.CreateLongerRecords([]*models.Collection{systemStats, containerStats})
}
})
scheduler.Start()
return nil
})
@@ -153,6 +156,8 @@ func (h *Hub) Run() {
})
// send test notification
e.Router.GET("/api/beszel/send-test-notification", h.am.SendTestNotification)
// API endpoint to get config.yml content
e.Router.GET("/api/beszel/config-yaml", h.getYamlConfig)
return nil
})
@@ -289,37 +294,61 @@ func (h *Hub) updateSystem(record *models.Record) {
return
}
// update system record
dao := h.app.Dao()
record.Set("status", "up")
record.Set("info", systemData.Info)
if err := h.app.Dao().SaveRecord(record); err != nil {
if err := dao.SaveRecord(record); err != nil {
h.app.Logger().Error("Failed to update record: ", "err", err.Error())
}
// add new system_stats record
system_stats, _ := h.app.Dao().FindCollectionByNameOrId("system_stats")
systemStatsRecord := models.NewRecord(system_stats)
systemStatsRecord.Set("system", record.Id)
systemStatsRecord.Set("stats", systemData.Stats)
systemStatsRecord.Set("type", "1m")
if err := h.app.Dao().SaveRecord(systemStatsRecord); err != nil {
h.app.Logger().Error("Failed to save record: ", "err", err.Error())
}
// add new container_stats record
if len(systemData.Containers) > 0 {
container_stats, _ := h.app.Dao().FindCollectionByNameOrId("container_stats")
containerStatsRecord := models.NewRecord(container_stats)
containerStatsRecord.Set("system", record.Id)
containerStatsRecord.Set("stats", systemData.Containers)
containerStatsRecord.Set("type", "1m")
if err := h.app.Dao().SaveRecord(containerStatsRecord); err != nil {
// add system_stats and container_stats records
if systemStats, containerStats, err := h.getCollections(); err != nil {
h.app.Logger().Error("Failed to get collections: ", "err", err.Error())
} else {
// add new system_stats record
systemStatsRecord := models.NewRecord(systemStats)
systemStatsRecord.Set("system", record.Id)
systemStatsRecord.Set("stats", systemData.Stats)
systemStatsRecord.Set("type", "1m")
if err := dao.SaveRecord(systemStatsRecord); err != nil {
h.app.Logger().Error("Failed to save record: ", "err", err.Error())
}
// add new container_stats record
if len(systemData.Containers) > 0 {
containerStatsRecord := models.NewRecord(containerStats)
containerStatsRecord.Set("system", record.Id)
containerStatsRecord.Set("stats", systemData.Containers)
containerStatsRecord.Set("type", "1m")
if err := dao.SaveRecord(containerStatsRecord); err != nil {
h.app.Logger().Error("Failed to save record: ", "err", err.Error())
}
}
}
// system info alerts (todo: extra fs alerts)
if err := h.am.HandleSystemAlerts(record, systemData.Info, systemData.Stats.Temperatures, systemData.Stats.ExtraFs); err != nil {
h.app.Logger().Error("System alerts error", "err", err.Error())
}
}
// return system_stats and container_stats collections
func (h *Hub) getCollections() (*models.Collection, *models.Collection, error) {
if h.systemStats == nil {
systemStats, err := h.app.Dao().FindCollectionByNameOrId("system_stats")
if err != nil {
return nil, nil, err
}
h.systemStats = systemStats
}
if h.containerStats == nil {
containerStats, err := h.app.Dao().FindCollectionByNameOrId("container_stats")
if err != nil {
return nil, nil, err
}
h.containerStats = containerStats
}
return h.systemStats, h.containerStats, nil
}
// set system to specified status and save record
func (h *Hub) updateSystemStatus(record *models.Record, status string) {
if record.GetString("status") != status {

View File

@@ -32,7 +32,7 @@ type RecordDeletionData struct {
retention time.Duration
}
type RecordStats []*struct {
type RecordStats []struct {
Stats []byte `db:"stats"`
}
@@ -41,9 +41,9 @@ func NewRecordManager(app *pocketbase.PocketBase) *RecordManager {
}
// Create longer records by averaging shorter records
func (rm *RecordManager) CreateLongerRecords() {
func (rm *RecordManager) CreateLongerRecords(collections []*models.Collection) {
// start := time.Now()
recordData := []LongerRecordData{
longerRecordData := []LongerRecordData{
{
shorterType: "1m",
// change to 9 from 10 to allow edge case timing or short pauses
@@ -78,17 +78,11 @@ func (rm *RecordManager) CreateLongerRecords() {
return err
}
// need *models.Collection to create a new record with models.NewRecord
collections := map[string]*models.Collection{}
for _, collectionName := range []string{"system_stats", "container_stats"} {
collection, _ := txDao.FindCollectionByNameOrId(collectionName)
collections[collectionName] = collection
}
// loop through all active systems, time periods, and collections
for _, system := range activeSystems {
// log.Println("processing system", system.GetString("name"))
for _, recordData := range recordData {
for i := range longerRecordData {
recordData := longerRecordData[i]
// log.Println("processing longer record type", recordData.longerType)
// add one minute padding for longer records because they are created slightly later than the job start time
longerRecordPeriod := time.Now().UTC().Add(recordData.longerTimeDuration + time.Minute)
@@ -112,14 +106,6 @@ func (rm *RecordManager) CreateLongerRecords() {
// get shorter records from the past x minutes
var stats RecordStats
// allShorterRecords, err := txDao.FindRecordsByExpr(
// collection,
// dbx.NewExp(
// "type = {:type} AND system = {:system} AND created > {:created}",
// dbx.Params{"type": recordData.shorterType, "system": system.Id, "created": shorterRecordPeriod},
// ),
// )
err := txDao.DB().
Select("stats").
From(collection.Name).
@@ -173,8 +159,8 @@ func (rm *RecordManager) AverageSystemStats(records RecordStats) system.Stats {
tempCount := float64(0)
var stats system.Stats
for _, record := range records {
json.Unmarshal(record.Stats, &stats)
for i := range records {
json.Unmarshal(records[i].Stats, &stats)
sum.Cpu += stats.Cpu
sum.Mem += stats.Mem
sum.MemUsed += stats.MemUsed
@@ -276,13 +262,14 @@ func (rm *RecordManager) AverageContainerStats(records RecordStats) []container.
count := float64(len(records))
var containerStats []container.Stats
for _, record := range records {
for i := range records {
// Reset the slice length to 0, but keep the capacity
containerStats = containerStats[:0]
if err := json.Unmarshal(record.Stats, &containerStats); err != nil {
if err := json.Unmarshal(records[i].Stats, &containerStats); err != nil {
return []container.Stats{}
}
for _, stat := range containerStats {
for i := range containerStats {
stat := containerStats[i]
if _, ok := sums[stat.Name]; !ok {
sums[stat.Name] = &container.Stats{Name: stat.Name}
}

8
beszel/site/.prettierrc Normal file
View File

@@ -0,0 +1,8 @@
{
"trailingComma": "es5",
"useTabs": true,
"tabWidth": 2,
"semi": false,
"singleQuote": false,
"printWidth": 120
}

Binary file not shown.

View File

@@ -1,17 +1,17 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "src/index.css",
"baseColor": "gray",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "src/index.css",
"baseColor": "gray",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}

View File

@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg" />

View File

@@ -0,0 +1,15 @@
import type { LinguiConfig } from "@lingui/conf"
const config: LinguiConfig = {
locales: ["en", "ar", "de", "es", "fr", "it", "ja", "ko", "pt", "tr", "ru", "uk", "vi", "zh-CN", "zh-HK"],
sourceLocale: "en",
compileNamespace: "ts",
catalogs: [
{
path: "<rootDir>/src/locales/{locale}/{locale}",
include: ["src"],
},
],
}
export default config

File diff suppressed because it is too large Load Diff

View File

@@ -5,16 +5,22 @@
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
"build": "lingui extract --overwrite && lingui compile && vite build",
"preview": "vite preview",
"sync": "lingui extract --overwrite && lingui compile",
"sync_and_purge": "lingui extract --overwrite --clean && lingui compile"
},
"dependencies": {
"@henrygd/queue": "^1.0.7",
"@lingui/detect-locale": "^4.13.0",
"@lingui/macro": "^4.13.0",
"@lingui/react": "^4.13.0",
"@nanostores/react": "^0.7.3",
"@nanostores/router": "^0.11.0",
"@radix-ui/react-alert-dialog": "^1.1.2",
"@radix-ui/react-checkbox": "^1.1.2",
"@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-direction": "^1.1.0",
"@radix-ui/react-dropdown-menu": "^2.1.2",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-select": "^2.1.2",
@@ -26,7 +32,6 @@
"@radix-ui/react-toast": "^1.2.2",
"@radix-ui/react-tooltip": "^1.1.3",
"@tanstack/react-table": "^8.20.5",
"@vitejs/plugin-react": "^4.3.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"cmdk": "^1.0.0",
@@ -42,12 +47,17 @@
"valibot": "^0.36.0"
},
"devDependencies": {
"@lingui/cli": "^4.13.0",
"@lingui/swc-plugin": "^4.1.0",
"@lingui/vite-plugin": "^4.13.0",
"@types/bun": "^1.1.11",
"@types/react": "^18.3.11",
"@types/react-dom": "^18.3.1",
"@vitejs/plugin-react-swc": "^3.7.1",
"autoprefixer": "^10.4.20",
"postcss": "^8.4.47",
"tailwindcss": "^3.4.14",
"tailwindcss-rtl": "^0.9.0",
"typescript": "^5.6.3",
"vite": "^5.4.9"
},
@@ -55,5 +65,8 @@
"@nanostores/router": {
"nanostores": "^0.11.3"
}
},
"optionalDependencies": {
"@esbuild/linux-arm64": "^0.21.5"
}
}

View File

@@ -1,6 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

View File

@@ -1,4 +1,4 @@
import { Button } from '@/components/ui/button'
import { Button } from "@/components/ui/button"
import {
Dialog,
DialogContent,
@@ -7,17 +7,19 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog'
import { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from '@/components/ui/tooltip'
} from "@/components/ui/dialog"
import { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { $publicKey, pb } from '@/lib/stores'
import { Copy, PlusIcon } from 'lucide-react'
import { useState, useRef, MutableRefObject } from 'react'
import { useStore } from '@nanostores/react'
import { cn, copyToClipboard, isReadOnlyUser } from '@/lib/utils'
import { navigate } from './router'
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { $publicKey, pb } from "@/lib/stores"
import { Copy, PlusIcon } from "lucide-react"
import { useState, useRef, MutableRefObject } from "react"
import { useStore } from "@nanostores/react"
import { cn, copyToClipboard, isReadOnlyUser } from "@/lib/utils"
import { navigate } from "./router"
import { Trans } from "@lingui/macro"
export function AddSystemButton({ className }: { className?: string }) {
const [open, setOpen] = useState(false)
@@ -37,8 +39,13 @@ export function AddSystemButton({ className }: { className?: string }) {
# - /mnt/disk1/.beszel:/extra-filesystems/disk1:ro
environment:
PORT: ${port}
KEY: "${publicKey}"
# FILESYSTEM: /dev/sda1 # override the root partition / device for disk I/O stats`)
KEY: "${publicKey}"`)
}
function copyInstallCommand(port: string) {
copyToClipboard(
`curl -sL https://raw.githubusercontent.com/henrygd/beszel/main/supplemental/scripts/install-agent.sh -o install-agent.sh && chmod +x install-agent.sh && ./install-agent.sh -p ${port} -k "${publicKey}"`
)
}
async function handleSubmit(e: SubmitEvent) {
@@ -48,8 +55,8 @@ export function AddSystemButton({ className }: { className?: string }) {
data.users = pb.authStore.model!.id
try {
setOpen(false)
await pb.collection('systems').create(data)
navigate('/')
await pb.collection("systems").create(data)
navigate("/")
// console.log(record)
} catch (e) {
console.log(e)
@@ -61,88 +68,119 @@ export function AddSystemButton({ className }: { className?: string }) {
<DialogTrigger asChild>
<Button
variant="outline"
className={cn('flex gap-1 max-xs:h-[2.4rem]', className, isReadOnlyUser() && 'hidden')}
className={cn("flex gap-1 max-xs:h-[2.4rem]", className, isReadOnlyUser() && "hidden")}
>
<PlusIcon className="h-4 w-4 -ml-1" />
Add <span className="hidden xs:inline">System</span>
<PlusIcon className="h-4 w-4 -ms-1" />
<Trans>
Add <span className="hidden sm:inline">System</span>
</Trans>
</Button>
</DialogTrigger>
<DialogContent className="w-[90%] sm:max-w-[425px] rounded-lg">
<DialogHeader>
<DialogTitle className="mb-2">Add New System</DialogTitle>
<DialogDescription>
The agent must be running on the system to connect. Copy the{' '}
<code className="bg-muted px-1 rounded-sm">docker-compose.yml</code> for the agent
below.
</DialogDescription>
</DialogHeader>
<form onSubmit={handleSubmit as any}>
<div className="grid gap-3 mt-1 mb-4">
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="name" className="text-right">
Name
</Label>
<Input id="name" name="name" className="col-span-3" required />
<DialogContent className="w-[90%] sm:max-w-[440px] rounded-lg">
<Tabs defaultValue="docker">
<DialogHeader>
<DialogTitle className="mb-2">
<Trans>Add New System</Trans>
</DialogTitle>
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="docker">Docker</TabsTrigger>
<TabsTrigger value="binary">
<Trans>Binary</Trans>
</TabsTrigger>
</TabsList>
</DialogHeader>
{/* Docker */}
<TabsContent value="docker">
<DialogDescription className="mb-4 leading-normal">
<Trans>
The agent must be running on the system to connect. Copy the
<code className="bg-muted px-1 rounded-sm leading-3">docker-compose.yml</code> for the agent below.
</Trans>
</DialogDescription>
</TabsContent>
{/* Binary */}
<TabsContent value="binary">
<DialogDescription className="mb-4 leading-normal">
<Trans>
The agent must be running on the system to connect. Copy the installation command for the agent below.
</Trans>
</DialogDescription>
</TabsContent>
<form onSubmit={handleSubmit as any}>
<div className="grid gap-3 mt-1 mb-4">
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="name" className="text-end">
<Trans>Name</Trans>
</Label>
<Input id="name" name="name" className="col-span-3" required />
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="host" className="text-end">
<Trans>Host / IP</Trans>
</Label>
<Input id="host" name="host" className="col-span-3" required />
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="port" className="text-end">
<Trans>Port</Trans>
</Label>
<Input ref={port} name="port" id="port" defaultValue="45876" className="col-span-3" required />
</div>
<div className="grid grid-cols-4 items-center gap-4 relative">
<Label htmlFor="pkey" className="text-end whitespace-pre">
<Trans comment="Use 'Key' if your language requires many more characters">Public Key</Trans>
</Label>
<Input readOnly id="pkey" value={publicKey} className="col-span-3" required></Input>
<div
className={
"h-6 w-24 bg-gradient-to-r rtl:bg-gradient-to-l from-transparent to-background to-65% absolute end-1 pointer-events-none"
}
></div>
<TooltipProvider delayDuration={100}>
<Tooltip>
<TooltipTrigger asChild>
<Button
type="button"
variant={"link"}
className="absolute end-0"
onClick={() => copyToClipboard(publicKey)}
>
<Copy className="h-4 w-4 " />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>
<Trans>Click to copy</Trans>
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="host" className="text-right">
Host / IP
</Label>
<Input id="host" name="host" className="col-span-3" required />
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="port" className="text-right">
Port
</Label>
<Input
ref={port}
name="port"
id="port"
defaultValue="45876"
className="col-span-3"
required
/>
</div>
<div className="grid grid-cols-4 items-center gap-4 relative">
<Label htmlFor="pkey" className="text-right whitespace-pre">
Public Key
</Label>
<Input readOnly id="pkey" value={publicKey} className="col-span-3" required></Input>
<div
className={
'h-6 w-24 bg-gradient-to-r from-transparent to-background to-65% absolute right-1 pointer-events-none'
}
></div>
<TooltipProvider delayDuration={100}>
<Tooltip>
<TooltipTrigger asChild>
<Button
type="button"
variant={'link'}
className="absolute right-0"
onClick={() => copyToClipboard(publicKey)}
>
<Copy className="h-4 w-4 " />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>Click to copy</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
</div>
<DialogFooter className="flex justify-end gap-2">
<Button
type="button"
variant={'ghost'}
onClick={() => copyDockerCompose(port.current.value)}
>
Copy docker compose
</Button>
<Button>Add system</Button>
</DialogFooter>
</form>
{/* Docker */}
<TabsContent value="docker">
<DialogFooter className="flex justify-end gap-2 sm:w-[calc(100%+20px)] sm:-ms-[20px]">
<Button type="button" variant={"ghost"} onClick={() => copyDockerCompose(port.current.value)}>
<Trans>Copy</Trans> docker compose
</Button>
<Button>
<Trans>Add system</Trans>
</Button>
</DialogFooter>
</TabsContent>
{/* Binary */}
<TabsContent value="binary">
<DialogFooter className="flex justify-end gap-2 sm:w-[calc(100%+20px)] sm:-ms-[20px]">
<Button type="button" variant={"ghost"} onClick={() => copyInstallCommand(port.current.value)}>
<Trans>Copy Linux command</Trans>
</Button>
<Button>
<Trans>Add system</Trans>
</Button>
</DialogFooter>
</TabsContent>
</form>
</Tabs>
</DialogContent>
</Dialog>
)

View File

@@ -1,6 +1,6 @@
import { memo, useState } from 'react'
import { useStore } from '@nanostores/react'
import { $alerts, $systems } from '@/lib/stores'
import { memo, useState } from "react"
import { useStore } from "@nanostores/react"
import { $alerts, $systems } from "@/lib/stores"
import {
Dialog,
DialogTrigger,
@@ -8,15 +8,16 @@ import {
DialogDescription,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog'
import { BellIcon, GlobeIcon, ServerIcon } from 'lucide-react'
import { alertInfo, cn } from '@/lib/utils'
import { Button } from '@/components/ui/button'
import { AlertRecord, SystemRecord } from '@/types'
import { Link } from '../router'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Checkbox } from '../ui/checkbox'
import { SystemAlert, SystemAlertGlobal } from './alerts-system'
} from "@/components/ui/dialog"
import { BellIcon, GlobeIcon, ServerIcon } from "lucide-react"
import { alertInfo, cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { AlertRecord, SystemRecord } from "@/types"
import { Link } from "../router"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Checkbox } from "../ui/checkbox"
import { SystemAlert, SystemAlertGlobal } from "./alerts-system"
import { Trans, t } from "@lingui/macro"
export default memo(function AlertsButton({ system }: { system: SystemRecord }) {
const alerts = useStore($alerts)
@@ -28,16 +29,10 @@ export default memo(function AlertsButton({ system }: { system: SystemRecord })
return (
<Dialog>
<DialogTrigger asChild>
<Button
variant="ghost"
size="icon"
aria-label="Alerts"
data-nolink
onClick={() => setOpened(true)}
>
<Button variant="ghost" size="icon" aria-label={t`Alerts`} data-nolink onClick={() => setOpened(true)}>
<BellIcon
className={cn('h-[1.2em] w-[1.2em] pointer-events-none', {
'fill-primary': active,
className={cn("h-[1.2em] w-[1.2em] pointer-events-none", {
"fill-primary": active,
})}
/>
</Button>
@@ -54,7 +49,7 @@ function TheContent({
}: {
data: { system: SystemRecord; alerts: AlertRecord[]; systemAlerts: AlertRecord[] }
}) {
const [overwriteExisting, setOverwriteExisting] = useState<boolean | 'indeterminate'>(false)
const [overwriteExisting, setOverwriteExisting] = useState<boolean | "indeterminate">(false)
const systems = $systems.get()
const data = Object.keys(alertInfo).map((key) => {
@@ -69,24 +64,28 @@ function TheContent({
return (
<>
<DialogHeader>
<DialogTitle className="text-xl">Alerts</DialogTitle>
<DialogTitle className="text-xl">
<Trans>Alerts</Trans>
</DialogTitle>
<DialogDescription>
See{' '}
<Link href="/settings/notifications" className="link">
notification settings
</Link>{' '}
to configure how you receive alerts.
<Trans>
See{" "}
<Link href="/settings/notifications" className="link">
notification settings
</Link>{" "}
to configure how you receive alerts.
</Trans>
</DialogDescription>
</DialogHeader>
<Tabs defaultValue="system">
<TabsList className="mb-1 -mt-0.5">
<TabsTrigger value="system">
<ServerIcon className="mr-2 h-3.5 w-3.5" />
<ServerIcon className="me-2 h-3.5 w-3.5" />
{system.name}
</TabsTrigger>
<TabsTrigger value="global">
<GlobeIcon className="mr-1.5 h-3.5 w-3.5" />
All systems
<GlobeIcon className="me-1.5 h-3.5 w-3.5" />
<Trans>All Systems</Trans>
</TabsTrigger>
</TabsList>
<TabsContent value="system">
@@ -107,17 +106,11 @@ function TheContent({
checked={overwriteExisting}
onCheckedChange={setOverwriteExisting}
/>
Overwrite existing alerts
<Trans>Overwrite existing alerts</Trans>
</label>
<div className="grid gap-3">
{data.map((d) => (
<SystemAlertGlobal
key={d.key}
data={d}
overwrite={overwriteExisting}
alerts={alerts}
systems={systems}
/>
<SystemAlertGlobal key={d.key} data={d} overwrite={overwriteExisting} alerts={alerts} systems={systems} />
))}
</div>
</TabsContent>

View File

@@ -1,11 +1,12 @@
import { pb } from '@/lib/stores'
import { alertInfo, cn } from '@/lib/utils'
import { Switch } from '@/components/ui/switch'
import { AlertRecord, SystemRecord } from '@/types'
import { lazy, Suspense, useRef, useState } from 'react'
import { toast } from '../ui/use-toast'
import { RecordOptions } from 'pocketbase'
import { newQueue, Queue } from '@henrygd/queue'
import { pb } from "@/lib/stores"
import { alertInfo, cn } from "@/lib/utils"
import { Switch } from "@/components/ui/switch"
import { AlertInfo, AlertRecord, SystemRecord } from "@/types"
import { lazy, Suspense, useRef, useState } from "react"
import { toast } from "../ui/use-toast"
import { RecordOptions } from "pocketbase"
import { newQueue, Queue } from "@henrygd/queue"
import { Trans, t, Plural } from "@lingui/macro"
interface AlertData {
checked?: boolean
@@ -13,19 +14,19 @@ interface AlertData {
min?: number
updateAlert?: (checked: boolean, value: number, min: number) => void
key: keyof typeof alertInfo
alert: (typeof alertInfo)[keyof typeof alertInfo]
alert: AlertInfo
system: SystemRecord
}
const Slider = lazy(() => import('@/components/ui/slider'))
const Slider = lazy(() => import("@/components/ui/slider"))
let queue: Queue
const failedUpdateToast = () =>
toast({
title: 'Failed to update alert',
description: 'Please check logs for more details.',
variant: 'destructive',
title: t`Failed to update alert`,
description: t`Please check logs for more details.`,
variant: "destructive",
})
export function SystemAlert({
@@ -42,11 +43,11 @@ export function SystemAlert({
data.updateAlert = async (checked: boolean, value: number, min: number) => {
try {
if (alert && !checked) {
await pb.collection('alerts').delete(alert.id)
await pb.collection("alerts").delete(alert.id)
} else if (alert && checked) {
await pb.collection('alerts').update(alert.id, { value, min, triggered: false })
await pb.collection("alerts").update(alert.id, { value, min, triggered: false })
} else if (checked) {
pb.collection('alerts').create({
pb.collection("alerts").create({
system: system.id,
user: pb.authStore.model!.id,
name: data.key,
@@ -75,7 +76,7 @@ export function SystemAlertGlobal({
systems,
}: {
data: AlertData
overwrite: boolean | 'indeterminate'
overwrite: boolean | "indeterminate"
alerts: AlertRecord[]
systems: SystemRecord[]
}) {
@@ -110,9 +111,7 @@ export function SystemAlertGlobal({
continue
}
// find matching existing alert
const existingAlert = alerts.find(
(alert) => alert.system === system.id && data.key === alert.name
)
const existingAlert = alerts.find((alert) => alert.system === system.id && data.key === alert.name)
// if first run, add system to set (alert already existed when global panel was opened)
if (existingAlert && !populatedSet && !overwrite) {
set.add(system.id)
@@ -127,13 +126,13 @@ export function SystemAlertGlobal({
if (existingAlert) {
// console.log('updating', system.name)
queue
.add(() => pb.collection('alerts').update(existingAlert.id, recordData, requestOptions))
.add(() => pb.collection("alerts").update(existingAlert.id, recordData, requestOptions))
.catch(failedUpdateToast)
} else {
// console.log('creating', system.name)
queue
.add(() =>
pb.collection('alerts').create(
pb.collection("alerts").create(
{
system: system.id,
user: pb.authStore.model!.id,
@@ -147,7 +146,7 @@ export function SystemAlertGlobal({
}
} else if (existingAlert) {
// console.log('deleting', system.name)
queue.add(() => pb.collection('alerts').delete(existingAlert.id)).catch(failedUpdateToast)
queue.add(() => pb.collection("alerts").delete(existingAlert.id)).catch(failedUpdateToast)
}
}
systemsWithExistingAlerts.current.populatedSet = true
@@ -159,7 +158,7 @@ export function SystemAlertGlobal({
function AlertContent({ data }: { data: AlertData }) {
const { key } = data
const hasSliders = !('single' in data.alert)
const hasSliders = !("single" in data.alert)
const [checked, setChecked] = useState(data.checked || false)
const [min, setMin] = useState(data.min || (hasSliders ? 10 : 0))
@@ -172,24 +171,21 @@ function AlertContent({ data }: { data: AlertData }) {
const Icon = alertInfo[key].icon
const updateAlert = (c?: boolean) =>
data.updateAlert?.(c ?? checked, newValue.current, newMin.current)
const updateAlert = (c?: boolean) => data.updateAlert?.(c ?? checked, newValue.current, newMin.current)
return (
<div className="rounded-lg border border-muted-foreground/15 hover:border-muted-foreground/20 transition-colors duration-100 group">
<label
htmlFor={`s${key}`}
className={cn('flex flex-row items-center justify-between gap-4 cursor-pointer p-4', {
'pb-0': showSliders,
className={cn("flex flex-row items-center justify-between gap-4 cursor-pointer p-4", {
"pb-0": showSliders,
})}
>
<div className="grid gap-1 select-none">
<p className="font-semibold flex gap-3 items-center capitalize">
<Icon className="h-4 w-4 opacity-85" /> {data.alert.name}
<p className="font-semibold flex gap-3 items-center">
<Icon className="h-4 w-4 opacity-85" /> {data.alert.name()}
</p>
{!showSliders && (
<span className="block text-sm text-muted-foreground">{data.alert.desc}</span>
)}
{!showSliders && <span className="block text-sm text-muted-foreground">{data.alert.desc()}</span>}
</div>
<Switch
id={`s${key}`}
@@ -205,11 +201,13 @@ function AlertContent({ data }: { data: AlertData }) {
<Suspense fallback={<div className="h-10" />}>
<div>
<p id={`v${key}`} className="text-sm block h-8">
Average exceeds{' '}
<strong className="text-foreground">
{value}
{data.alert.unit}
</strong>
<Trans>
Average exceeds{" "}
<strong className="text-foreground">
{value}
{data.alert.unit}
</strong>
</Trans>
</p>
<div className="flex gap-3">
<Slider
@@ -218,14 +216,16 @@ function AlertContent({ data }: { data: AlertData }) {
onValueCommit={(val) => (newValue.current = val[0]) && updateAlert()}
onValueChange={(val) => setValue(val[0])}
min={1}
max={99}
max={alertInfo[key].max ?? 99}
/>
</div>
</div>
<div>
<p id={`t${key}`} className="text-sm block h-8">
For <strong className="text-foreground">{min}</strong> minute
{min > 1 && 's'}
<Trans>
For <strong className="text-foreground">{min}</strong>{" "}
<Plural value={min} one=" minute" other=" minutes" />
</Trans>
</p>
<div className="flex gap-3">
<Slider

View File

@@ -1,6 +1,6 @@
import { Area, AreaChart, CartesianGrid, YAxis } from 'recharts'
import { Area, AreaChart, CartesianGrid, YAxis } from "recharts"
import { ChartContainer, ChartTooltip, ChartTooltipContent, xAxis } from '@/components/ui/chart'
import { ChartContainer, ChartTooltip, ChartTooltipContent, xAxis } from "@/components/ui/chart"
import {
useYAxisWidth,
cn,
@@ -8,10 +8,12 @@ import {
toFixedWithoutTrailingZeros,
decimalString,
chartMargin,
} from '@/lib/utils'
} from "@/lib/utils"
// import Spinner from '../spinner'
import { ChartData } from '@/types'
import { memo, useMemo } from 'react'
import { ChartData } from "@/types"
import { memo, useMemo } from "react"
import { t } from "@lingui/macro"
import { useLingui } from "@lingui/react"
/** [label, key, color, opacity] */
type DataKeys = [string, string, number, number]
@@ -21,14 +23,14 @@ const getNestedValue = (path: string, max = false, data: any): number | null =>
// a max value which doesn't exist, or the value was zero and omitted from the stats object.
// so we check if cpum is present. if so, return 0 to make sure the zero value is displayed.
// if not, return null - there is no max data so do not display anything.
return `stats.${path}${max ? 'm' : ''}`
.split('.')
return `stats.${path}${max ? "m" : ""}`
.split(".")
.reduce((acc: any, key: string) => acc?.[key] ?? (data.stats?.cpum ? 0 : null), data)
}
export default memo(function AreaChartDefault({
maxToggled = false,
unit = ' MB/s',
unit = " MB/s",
chartName,
chartData,
}: {
@@ -38,46 +40,53 @@ export default memo(function AreaChartDefault({
chartData: ChartData
}) {
const { yAxisWidth, updateYAxisWidth } = useYAxisWidth()
const { i18n } = useLingui()
const { chartTime } = chartData
const showMax = chartTime !== '1h' && maxToggled
const showMax = chartTime !== "1h" && maxToggled
const dataKeys: DataKeys[] = useMemo(() => {
// [label, key, color, opacity]
if (chartName === 'CPU Usage') {
return [[chartName, 'cpu', 1, 0.4]]
} else if (chartName === 'dio') {
if (chartName === "CPU Usage") {
return [[t`CPU Usage`, "cpu", 1, 0.4]]
} else if (chartName === "dio") {
return [
['Write', 'dw', 3, 0.3],
['Read', 'dr', 1, 0.3],
[t({ message: "Write", comment: "Context is disk write" }), "dw", 3, 0.3],
[t({ message: "Read", comment: "Context is disk read" }), "dr", 1, 0.3],
]
} else if (chartName === 'bw') {
} else if (chartName === "bw") {
return [
['Sent', 'ns', 5, 0.2],
['Received', 'nr', 2, 0.2],
[t({ message: "Sent", comment: "Context is network bytes sent (upload)" }), "ns", 5, 0.2],
[t({ message: "Received", comment: "Context is network bytes received (download)" }), "nr", 2, 0.2],
]
} else if (chartName.startsWith('efs')) {
} else if (chartName.startsWith("efs")) {
return [
['Write', `${chartName}.w`, 3, 0.3],
['Read', `${chartName}.r`, 1, 0.3],
[t`Write`, `${chartName}.w`, 3, 0.3],
[t`Read`, `${chartName}.r`, 1, 0.3],
]
}
return []
}, [])
}, [chartName, i18n.locale])
// console.log('Rendered at', new Date())
if (chartData.systemStats.length === 0) {
return null
}
return (
<div>
<ChartContainer
className={cn('h-full w-full absolute aspect-auto bg-card opacity-0 transition-opacity', {
'opacity-100': yAxisWidth,
className={cn("h-full w-full absolute aspect-auto bg-card opacity-0 transition-opacity", {
"opacity-100": yAxisWidth,
})}
>
<AreaChart accessibilityLayer data={chartData.systemStats} margin={chartMargin}>
<CartesianGrid vertical={false} />
<YAxis
direction="ltr"
orientation={chartData.orientation}
className="tracking-tighter"
width={yAxisWidth}
tickFormatter={(value) => {

View File

@@ -1,33 +1,23 @@
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
import { $chartTime } from '@/lib/stores'
import { chartTimeData, cn } from '@/lib/utils'
import { ChartTimes } from '@/types'
import { useStore } from '@nanostores/react'
import { HistoryIcon } from 'lucide-react'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { $chartTime } from "@/lib/stores"
import { chartTimeData, cn } from "@/lib/utils"
import { ChartTimes } from "@/types"
import { useStore } from "@nanostores/react"
import { HistoryIcon } from "lucide-react"
export default function ChartTimeSelect({ className }: { className?: string }) {
const chartTime = useStore($chartTime)
return (
<Select
defaultValue="1h"
value={chartTime}
onValueChange={(value: ChartTimes) => $chartTime.set(value)}
>
<SelectTrigger className={cn(className, 'relative pl-10 pr-5')}>
<HistoryIcon className="h-4 w-4 absolute left-4 top-1/2 -translate-y-1/2 opacity-85" />
<Select defaultValue="1h" value={chartTime} onValueChange={(value: ChartTimes) => $chartTime.set(value)}>
<SelectTrigger className={cn(className, "relative ps-10 pe-5")}>
<HistoryIcon className="h-4 w-4 absolute start-4 top-1/2 -translate-y-1/2 opacity-85" />
<SelectValue />
</SelectTrigger>
<SelectContent>
{Object.entries(chartTimeData).map(([value, { label }]) => (
<SelectItem key={label} value={value}>
{label}
<SelectItem key={value} value={value}>
{label()}
</SelectItem>
))}
</SelectContent>

View File

@@ -1,12 +1,6 @@
import { Area, AreaChart, CartesianGrid, YAxis } from 'recharts'
import {
ChartConfig,
ChartContainer,
ChartTooltip,
ChartTooltipContent,
xAxis,
} from '@/components/ui/chart'
import { memo, useMemo } from 'react'
import { Area, AreaChart, CartesianGrid, YAxis } from "recharts"
import { ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent, xAxis } from "@/components/ui/chart"
import { memo, useMemo } from "react"
import {
useYAxisWidth,
cn,
@@ -16,18 +10,18 @@ import {
toFixedFloat,
getSizeAndUnit,
toFixedWithoutTrailingZeros,
} from '@/lib/utils'
} from "@/lib/utils"
// import Spinner from '../spinner'
import { useStore } from '@nanostores/react'
import { $containerFilter } from '@/lib/stores'
import { ChartData } from '@/types'
import { Separator } from '../ui/separator'
import { useStore } from "@nanostores/react"
import { $containerFilter } from "@/lib/stores"
import { ChartData } from "@/types"
import { Separator } from "../ui/separator"
export default memo(function ContainerChart({
dataKey,
chartData,
chartName,
unit = '%',
unit = "%",
}: {
dataKey: string
chartData: ChartData
@@ -39,7 +33,7 @@ export default memo(function ContainerChart({
const { containerData } = chartData
const isNetChart = chartName === 'net'
const isNetChart = chartName === "net"
const chartConfig = useMemo(() => {
let config = {} as Record<
@@ -52,7 +46,7 @@ export default memo(function ContainerChart({
const totalUsage = {} as Record<string, number>
for (let stats of containerData) {
for (let key in stats) {
if (!key || key === 'created') {
if (!key || key === "created") {
continue
}
if (!(key in totalUsage)) {
@@ -87,7 +81,7 @@ export default memo(function ContainerChart({
tickFormatter: (value: any) => string
}
// tick formatter
if (chartName === 'cpu') {
if (chartName === "cpu") {
obj.tickFormatter = (value) => {
const val = toFixedWithoutTrailingZeros(value, 2) + unit
return updateYAxisWidth(val)
@@ -95,7 +89,7 @@ export default memo(function ContainerChart({
} else {
obj.tickFormatter = (value) => {
const { v, u } = getSizeAndUnit(value, false)
return updateYAxisWidth(`${toFixedFloat(v, 2)}${u}${isNetChart ? '/s' : ''}`)
return updateYAxisWidth(`${toFixedFloat(v, 2)}${u}${isNetChart ? "/s" : ""}`)
}
}
// tooltip formatter
@@ -107,10 +101,10 @@ export default memo(function ContainerChart({
return (
<span className="flex">
{decimalString(received)} MB/s
<span className="opacity-70 ml-0.5"> rx </span>
<span className="opacity-70 ms-0.5"> rx </span>
<Separator orientation="vertical" className="h-3 mx-1.5 bg-primary/40" />
{decimalString(sent)} MB/s
<span className="opacity-70 ml-0.5"> tx</span>
<span className="opacity-70 ms-0.5"> tx</span>
</span>
)
} catch (e) {
@@ -122,20 +116,24 @@ export default memo(function ContainerChart({
}
// data function
if (isNetChart) {
obj.dataFunction = (key: string, data: any) => (data[key]?.nr ?? 0) + (data[key]?.ns ?? 0)
obj.dataFunction = (key: string, data: any) => (data[key]?.nr ?? null) + (data[key]?.ns ?? null)
} else {
obj.dataFunction = (key: string, data: any) => data[key]?.[dataKey] ?? 0
obj.dataFunction = (key: string, data: any) => data[key]?.[dataKey] ?? null
}
return obj
}, [])
// console.log('rendered at', new Date())
if (containerData.length === 0) {
return null
}
return (
<div>
<ChartContainer
className={cn('h-full w-full absolute aspect-auto bg-card opacity-0 transition-opacity', {
'opacity-100': yAxisWidth,
className={cn("h-full w-full absolute aspect-auto bg-card opacity-0 transition-opacity", {
"opacity-100": yAxisWidth,
})}
>
<AreaChart
@@ -147,6 +145,8 @@ export default memo(function ContainerChart({
>
<CartesianGrid vertical={false} />
<YAxis
direction="ltr"
orientation={chartData.orientation}
className="tracking-tighter"
width={yAxisWidth}
tickFormatter={tickFormatter}

View File

@@ -1,6 +1,6 @@
import { Area, AreaChart, CartesianGrid, YAxis } from 'recharts'
import { Area, AreaChart, CartesianGrid, YAxis } from "recharts"
import { ChartContainer, ChartTooltip, ChartTooltipContent, xAxis } from '@/components/ui/chart'
import { ChartContainer, ChartTooltip, ChartTooltipContent, xAxis } from "@/components/ui/chart"
import {
useYAxisWidth,
cn,
@@ -9,9 +9,11 @@ import {
toFixedFloat,
chartMargin,
getSizeAndUnit,
} from '@/lib/utils'
import { ChartData } from '@/types'
import { memo } from 'react'
} from "@/lib/utils"
import { ChartData } from "@/types"
import { memo } from "react"
import { t } from "@lingui/macro"
import { useLingui } from "@lingui/react"
export default memo(function DiskChart({
dataKey,
@@ -23,17 +25,24 @@ export default memo(function DiskChart({
chartData: ChartData
}) {
const { yAxisWidth, updateYAxisWidth } = useYAxisWidth()
const { _ } = useLingui()
if (chartData.systemStats.length === 0) {
return null
}
return (
<div>
<ChartContainer
className={cn('h-full w-full absolute aspect-auto bg-card opacity-0 transition-opacity', {
'opacity-100': yAxisWidth,
className={cn("h-full w-full absolute aspect-auto bg-card opacity-0 transition-opacity", {
"opacity-100": yAxisWidth,
})}
>
<AreaChart accessibilityLayer data={chartData.systemStats} margin={chartMargin}>
<CartesianGrid vertical={false} />
<YAxis
direction="ltr"
orientation={chartData.orientation}
className="tracking-tighter"
width={yAxisWidth}
domain={[0, diskSize]}
@@ -62,7 +71,7 @@ export default memo(function DiskChart({
/>
<Area
dataKey={dataKey}
name="Disk Usage"
name={_(t`Disk Usage`)}
type="monotoneX"
fill="hsl(var(--chart-4))"
fillOpacity={0.4}

View File

@@ -1,36 +1,38 @@
import { Area, AreaChart, CartesianGrid, YAxis } from 'recharts'
import { Area, AreaChart, CartesianGrid, YAxis } from "recharts"
import { ChartContainer, ChartTooltip, ChartTooltipContent, xAxis } from '@/components/ui/chart'
import {
useYAxisWidth,
cn,
toFixedFloat,
decimalString,
formatShortDate,
chartMargin,
} from '@/lib/utils'
import { memo } from 'react'
import { ChartData } from '@/types'
import { ChartContainer, ChartTooltip, ChartTooltipContent, xAxis } from "@/components/ui/chart"
import { useYAxisWidth, cn, toFixedFloat, decimalString, formatShortDate, chartMargin } from "@/lib/utils"
import { memo } from "react"
import { ChartData } from "@/types"
import { t } from "@lingui/macro"
import { useLingui } from "@lingui/react"
export default memo(function MemChart({ chartData }: { chartData: ChartData }) {
const { yAxisWidth, updateYAxisWidth } = useYAxisWidth()
const { _ } = useLingui()
const totalMem = toFixedFloat(chartData.systemStats.at(-1)?.stats.m ?? 0, 1)
// console.log('rendered at', new Date())
if (chartData.systemStats.length === 0) {
return null
}
return (
<div>
{/* {!yAxisSet && <Spinner />} */}
<ChartContainer
className={cn('h-full w-full absolute aspect-auto bg-card opacity-0 transition-opacity', {
'opacity-100': yAxisWidth,
className={cn("h-full w-full absolute aspect-auto bg-card opacity-0 transition-opacity", {
"opacity-100": yAxisWidth,
})}
>
<AreaChart accessibilityLayer data={chartData.systemStats} margin={chartMargin}>
<CartesianGrid vertical={false} />
{totalMem && (
<YAxis
direction="ltr"
orientation={chartData.orientation}
// use "ticks" instead of domain / tickcount if need more control
domain={[0, totalMem]}
tickCount={9}
@@ -40,7 +42,7 @@ export default memo(function MemChart({ chartData }: { chartData: ChartData }) {
axisLine={false}
tickFormatter={(value) => {
const val = toFixedFloat(value, 1)
return updateYAxisWidth(val + ' GB')
return updateYAxisWidth(val + " GB")
}}
/>
)}
@@ -54,13 +56,13 @@ export default memo(function MemChart({ chartData }: { chartData: ChartData }) {
// @ts-ignore
itemSorter={(a, b) => a.order - b.order}
labelFormatter={(_, data) => formatShortDate(data[0].payload.created)}
contentFormatter={(item) => decimalString(item.value) + ' GB'}
contentFormatter={(item) => decimalString(item.value) + " GB"}
// indicator="line"
/>
}
/>
<Area
name="Used"
name={_(t`Used`)}
order={3}
dataKey="stats.mu"
type="monotoneX"
@@ -84,7 +86,7 @@ export default memo(function MemChart({ chartData }: { chartData: ChartData }) {
/>
)}
<Area
name="Cache / Buffers"
name={_(t`Cache / Buffers`)}
order={1}
dataKey="stats.mb"
type="monotoneX"

View File

@@ -1,6 +1,6 @@
import { Area, AreaChart, CartesianGrid, YAxis } from 'recharts'
import { Area, AreaChart, CartesianGrid, YAxis } from "recharts"
import { ChartContainer, ChartTooltip, ChartTooltipContent, xAxis } from '@/components/ui/chart'
import { ChartContainer, ChartTooltip, ChartTooltipContent, xAxis } from "@/components/ui/chart"
import {
useYAxisWidth,
cn,
@@ -8,32 +8,36 @@ import {
toFixedWithoutTrailingZeros,
decimalString,
chartMargin,
} from '@/lib/utils'
import { ChartData } from '@/types'
import { memo } from 'react'
} from "@/lib/utils"
import { ChartData } from "@/types"
import { memo } from "react"
import { t } from "@lingui/macro"
export default memo(function SwapChart({ chartData }: { chartData: ChartData }) {
const { yAxisWidth, updateYAxisWidth } = useYAxisWidth()
if (chartData.systemStats.length === 0) {
return null
}
return (
<div>
<ChartContainer
className={cn('h-full w-full absolute aspect-auto bg-card opacity-0 transition-opacity', {
'opacity-100': yAxisWidth,
className={cn("h-full w-full absolute aspect-auto bg-card opacity-0 transition-opacity", {
"opacity-100": yAxisWidth,
})}
>
<AreaChart accessibilityLayer data={chartData.systemStats} margin={chartMargin}>
<CartesianGrid vertical={false} />
<YAxis
direction="ltr"
orientation={chartData.orientation}
className="tracking-tighter"
domain={[
0,
() => toFixedWithoutTrailingZeros(chartData.systemStats.at(-1)?.stats.s ?? 0.04, 2),
]}
domain={[0, () => toFixedWithoutTrailingZeros(chartData.systemStats.at(-1)?.stats.s ?? 0.04, 2)]}
width={yAxisWidth}
tickLine={false}
axisLine={false}
tickFormatter={(value) => updateYAxisWidth(value + ' GB')}
tickFormatter={(value) => updateYAxisWidth(value + " GB")}
/>
{xAxis(chartData)}
<ChartTooltip
@@ -42,14 +46,14 @@ export default memo(function SwapChart({ chartData }: { chartData: ChartData })
content={
<ChartTooltipContent
labelFormatter={(_, data) => formatShortDate(data[0].payload.created)}
contentFormatter={(item) => decimalString(item.value) + ' GB'}
contentFormatter={(item) => decimalString(item.value) + " GB"}
// indicator="line"
/>
}
/>
<Area
dataKey="stats.su"
name="Swap Usage"
name={t`Used`}
type="monotoneX"
fill="hsl(var(--chart-2))"
fillOpacity={0.4}

View File

@@ -1,4 +1,4 @@
import { CartesianGrid, Line, LineChart, YAxis } from 'recharts'
import { CartesianGrid, Line, LineChart, YAxis } from "recharts"
import {
ChartContainer,
@@ -7,7 +7,7 @@ import {
ChartTooltip,
ChartTooltipContent,
xAxis,
} from '@/components/ui/chart'
} from "@/components/ui/chart"
import {
useYAxisWidth,
cn,
@@ -15,13 +15,17 @@ import {
toFixedWithoutTrailingZeros,
decimalString,
chartMargin,
} from '@/lib/utils'
import { ChartData } from '@/types'
import { memo, useMemo } from 'react'
} from "@/lib/utils"
import { ChartData } from "@/types"
import { memo, useMemo } from "react"
export default memo(function TemperatureChart({ chartData }: { chartData: ChartData }) {
const { yAxisWidth, updateYAxisWidth } = useYAxisWidth()
if (chartData.systemStats.length === 0) {
return null
}
/** Format temperature data for chart and assign colors */
const newChartData = useMemo(() => {
const newChartData = { data: [], colors: {} } as {
@@ -53,19 +57,21 @@ export default memo(function TemperatureChart({ chartData }: { chartData: ChartD
return (
<div>
<ChartContainer
className={cn('h-full w-full absolute aspect-auto bg-card opacity-0 transition-opacity', {
'opacity-100': yAxisWidth,
className={cn("h-full w-full absolute aspect-auto bg-card opacity-0 transition-opacity", {
"opacity-100": yAxisWidth,
})}
>
<LineChart accessibilityLayer data={newChartData.data} margin={chartMargin}>
<CartesianGrid vertical={false} />
<YAxis
direction="ltr"
orientation={chartData.orientation}
className="tracking-tighter"
domain={[0, 'auto']}
domain={[0, "auto"]}
width={yAxisWidth}
tickFormatter={(value) => {
const val = toFixedWithoutTrailingZeros(value, 2)
return updateYAxisWidth(val + ' °C')
return updateYAxisWidth(val + " °C")
}}
tickLine={false}
axisLine={false}
@@ -79,7 +85,7 @@ export default memo(function TemperatureChart({ chartData }: { chartData: ChartD
content={
<ChartTooltipContent
labelFormatter={(_, data) => formatShortDate(data[0].payload.created)}
contentFormatter={(item) => decimalString(item.value) + ' °C'}
contentFormatter={(item) => decimalString(item.value) + " °C"}
// indicator="line"
/>
}

View File

@@ -8,7 +8,7 @@ import {
Server,
SettingsIcon,
UsersIcon,
} from 'lucide-react'
} from "lucide-react"
import {
CommandDialog,
@@ -19,34 +19,36 @@ import {
CommandList,
CommandSeparator,
CommandShortcut,
} from '@/components/ui/command'
import { useEffect, useState } from 'react'
import { useStore } from '@nanostores/react'
import { $systems } from '@/lib/stores'
import { isAdmin } from '@/lib/utils'
import { navigate } from './router'
} from "@/components/ui/command"
import { useEffect } from "react"
import { useStore } from "@nanostores/react"
import { $systems } from "@/lib/stores"
import { isAdmin } from "@/lib/utils"
import { navigate } from "./router"
import { Trans, t } from "@lingui/macro"
export default function CommandPalette() {
const [open, setOpen] = useState(false)
export default function CommandPalette({ open, setOpen }: { open: boolean; setOpen: (open: boolean) => void }) {
const systems = useStore($systems)
useEffect(() => {
const down = (e: KeyboardEvent) => {
if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
e.preventDefault()
setOpen((open) => !open)
setOpen(!open)
}
}
document.addEventListener('keydown', down)
return () => document.removeEventListener('keydown', down)
}, [])
document.addEventListener("keydown", down)
return () => document.removeEventListener("keydown", down)
}, [open, setOpen])
return (
<CommandDialog open={open} onOpenChange={setOpen}>
<CommandInput placeholder="Search for systems or settings..." />
<CommandInput placeholder={t`Search for systems or settings...`} />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
<CommandEmpty>
<Trans>No results found.</Trans>
</CommandEmpty>
{systems.length > 0 && (
<>
<CommandGroup>
@@ -58,7 +60,7 @@ export default function CommandPalette() {
setOpen(false)
}}
>
<Server className="mr-2 h-4 w-4" />
<Server className="me-2 h-4 w-4" />
<span>{system.name}</span>
<CommandShortcut>{system.host}</CommandShortcut>
</CommandItem>
@@ -67,106 +69,140 @@ export default function CommandPalette() {
<CommandSeparator className="mb-1.5" />
</>
)}
<CommandGroup heading="Pages / Settings">
<CommandGroup heading={t`Pages / Settings`}>
<CommandItem
keywords={['home']}
keywords={["home"]}
onSelect={() => {
navigate('/')
setOpen((open) => !open)
navigate("/")
setOpen(false)
}}
>
<LayoutDashboard className="mr-2 h-4 w-4" />
<span>Dashboard</span>
<CommandShortcut>Page</CommandShortcut>
<LayoutDashboard className="me-2 h-4 w-4" />
<span>
<Trans>Dashboard</Trans>
</span>
<CommandShortcut>
<Trans>Page</Trans>
</CommandShortcut>
</CommandItem>
<CommandItem
onSelect={() => {
navigate('/settings/general')
setOpen((open) => !open)
navigate("/settings/general")
setOpen(false)
}}
>
<SettingsIcon className="mr-2 h-4 w-4" />
<span>Settings</span>
<CommandShortcut>Settings</CommandShortcut>
<SettingsIcon className="me-2 h-4 w-4" />
<span>
<Trans>Settings</Trans>
</span>
<CommandShortcut>
<Trans>Settings</Trans>
</CommandShortcut>
</CommandItem>
<CommandItem
keywords={['alerts']}
keywords={["alerts"]}
onSelect={() => {
navigate('/settings/notifications')
setOpen((open) => !open)
navigate("/settings/notifications")
setOpen(false)
}}
>
<MailIcon className="mr-2 h-4 w-4" />
<span>Notification settings</span>
<CommandShortcut>Settings</CommandShortcut>
<MailIcon className="me-2 h-4 w-4" />
<span>
<Trans>Notifications</Trans>
</span>
<CommandShortcut>
<Trans>Settings</Trans>
</CommandShortcut>
</CommandItem>
<CommandItem
keywords={['github']}
keywords={["github"]}
onSelect={() => {
window.location.href = 'https://github.com/henrygd/beszel/blob/main/readme.md'
window.location.href = "https://github.com/henrygd/beszel/blob/main/readme.md"
}}
>
<Github className="mr-2 h-4 w-4" />
<span>Documentation</span>
<Github className="me-2 h-4 w-4" />
<span>
<Trans>Documentation</Trans>
</span>
<CommandShortcut>GitHub</CommandShortcut>
</CommandItem>
</CommandGroup>
{isAdmin() && (
<>
<CommandSeparator className="mb-1.5" />
<CommandGroup heading="Admin">
<CommandGroup heading={t`Admin`}>
<CommandItem
keywords={['pocketbase']}
keywords={["pocketbase"]}
onSelect={() => {
setOpen(false)
window.open('/_/', '_blank')
window.open("/_/", "_blank")
}}
>
<UsersIcon className="mr-2 h-4 w-4" />
<span>Users</span>
<CommandShortcut>Admin</CommandShortcut>
<UsersIcon className="me-2 h-4 w-4" />
<span>
<Trans>Users</Trans>
</span>
<CommandShortcut>
<Trans>Admin</Trans>
</CommandShortcut>
</CommandItem>
<CommandItem
onSelect={() => {
setOpen(false)
window.open('/_/#/logs', '_blank')
window.open("/_/#/logs", "_blank")
}}
>
<LogsIcon className="mr-2 h-4 w-4" />
<span>Logs</span>
<CommandShortcut>Admin</CommandShortcut>
<LogsIcon className="me-2 h-4 w-4" />
<span>
<Trans>Logs</Trans>
</span>
<CommandShortcut>
<Trans>Admin</Trans>
</CommandShortcut>
</CommandItem>
<CommandItem
onSelect={() => {
setOpen(false)
window.open('/_/#/settings/backups', '_blank')
window.open("/_/#/settings/backups", "_blank")
}}
>
<DatabaseBackupIcon className="mr-2 h-4 w-4" />
<span>Backups</span>
<CommandShortcut>Admin</CommandShortcut>
<DatabaseBackupIcon className="me-2 h-4 w-4" />
<span>
<Trans>Backups</Trans>
</span>
<CommandShortcut>
<Trans>Admin</Trans>
</CommandShortcut>
</CommandItem>
<CommandItem
keywords={['oauth', 'oicd']}
keywords={["oauth", "oicd"]}
onSelect={() => {
setOpen(false)
window.open('/_/#/settings/auth-providers', '_blank')
window.open("/_/#/settings/auth-providers", "_blank")
}}
>
<LockKeyholeIcon className="mr-2 h-4 w-4" />
<span>Auth Providers</span>
<CommandShortcut>Admin</CommandShortcut>
<LockKeyholeIcon className="me-2 h-4 w-4" />
<span>
<Trans>Auth Providers</Trans>
</span>
<CommandShortcut>
<Trans>Admin</Trans>
</CommandShortcut>
</CommandItem>
<CommandItem
keywords={['email']}
keywords={["email"]}
onSelect={() => {
setOpen(false)
window.open('/_/#/settings/mail', '_blank')
window.open("/_/#/settings/mail", "_blank")
}}
>
<MailIcon className="mr-2 h-4 w-4" />
<span>SMTP settings</span>
<CommandShortcut>Admin</CommandShortcut>
<MailIcon className="me-2 h-4 w-4" />
<span>
<Trans>SMTP settings</Trans>
</span>
<CommandShortcut>
<Trans>Admin</Trans>
</CommandShortcut>
</CommandItem>
</CommandGroup>
</>

View File

@@ -1,20 +1,22 @@
import { useEffect, useMemo, useRef } from 'react'
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from './ui/dialog'
import { Textarea } from './ui/textarea'
import { $copyContent } from '@/lib/stores'
import { useEffect, useMemo, useRef } from "react"
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "./ui/dialog"
import { Textarea } from "./ui/textarea"
import { $copyContent } from "@/lib/stores"
import { Trans } from "@lingui/macro"
export default function CopyToClipboard({ content }: { content: string }) {
return (
<Dialog defaultOpen={true}>
<DialogContent className="w-[90%] rounded-lg" style={{ maxWidth: 530 }}>
<DialogContent className="w-[90%] rounded-lg md:pt-4" style={{ maxWidth: 530 }}>
<DialogHeader>
<DialogTitle>Could not copy to clipboard</DialogTitle>
<DialogDescription>Please copy the text manually.</DialogDescription>
<DialogTitle>
<Trans>Copy text</Trans>
</DialogTitle>
<DialogDescription className="hidden xs:block">
<Trans>Automatic copy requires a secure context.</Trans>
</DialogDescription>
</DialogHeader>
<CopyTextarea content={content} />
<p className="text-sm text-muted-foreground">
Clipboard API requires a secure context (https, localhost, or *.localhost)
</p>
</DialogContent>
</Dialog>
)
@@ -24,7 +26,7 @@ function CopyTextarea({ content }: { content: string }) {
const textareaRef = useRef<HTMLTextAreaElement>(null)
const rows = useMemo(() => {
return content.split('\n').length
return content.split("\n").length
}, [content])
useEffect(() => {
@@ -34,7 +36,7 @@ function CopyTextarea({ content }: { content: string }) {
}, [textareaRef])
useEffect(() => {
return () => $copyContent.set('')
return () => $copyContent.set("")
}, [])
return (

View File

@@ -0,0 +1,34 @@
import { LanguagesIcon } from "lucide-react"
import { Button } from "@/components/ui/button"
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
import languages from "@/lib/languages"
import { cn } from "@/lib/utils"
import { useLingui } from "@lingui/react"
import { dynamicActivate } from "@/lib/i18n"
export function LangToggle() {
const { i18n } = useLingui()
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant={"ghost"} size="icon" className="hidden 450:flex">
<LanguagesIcon className="absolute h-[1.2rem] w-[1.2rem] light:opacity-85" />
<span className="sr-only">Language</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="grid grid-cols-3">
{languages.map(({ lang, label, e }) => (
<DropdownMenuItem
key={lang}
className={cn("px-3 flex gap-2.5", lang === i18n.locale && "font-semibold")}
onClick={() => dynamicActivate(lang)}
>
<span>{e}</span> {label}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
)
}

View File

@@ -1,28 +1,20 @@
import { cn } from '@/lib/utils'
import { buttonVariants } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { LoaderCircle, LockIcon, LogInIcon, MailIcon, UserIcon } from 'lucide-react'
import { $authenticated, pb } from '@/lib/stores'
import * as v from 'valibot'
import { toast } from '../ui/use-toast'
import {
Dialog,
DialogContent,
DialogTrigger,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog'
import { useCallback, useState } from 'react'
import { AuthMethodsList, OAuth2AuthConfig } from 'pocketbase'
import { Link } from '../router'
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { LoaderCircle, LockIcon, LogInIcon, MailIcon, UserIcon } from "lucide-react"
import { $authenticated, pb } from "@/lib/stores"
import * as v from "valibot"
import { toast } from "../ui/use-toast"
import { Dialog, DialogContent, DialogTrigger, DialogHeader, DialogTitle } from "@/components/ui/dialog"
import { useCallback, useState } from "react"
import { AuthMethodsList, OAuth2AuthConfig } from "pocketbase"
import { Link } from "../router"
import { Trans, t } from "@lingui/macro"
const honeypot = v.literal('')
const emailSchema = v.pipe(v.string(), v.email('Invalid email address.'))
const passwordSchema = v.pipe(
v.string(),
v.minLength(10, 'Password must be at least 10 characters.')
)
const honeypot = v.literal("")
const emailSchema = v.pipe(v.string(), v.email(t`Invalid email address.`))
const passwordSchema = v.pipe(v.string(), v.minLength(10, t`Password must be at least 10 characters.`))
const LoginSchema = v.looseObject({
name: honeypot,
@@ -36,9 +28,9 @@ const RegisterSchema = v.looseObject({
v.string(),
v.regex(
/^(?=.*[a-zA-Z])[a-zA-Z0-9_-]+$/,
'Invalid username. You may use alphanumeric characters, underscores, and hyphens.'
"Invalid username. You may use alphanumeric characters, underscores, and hyphens."
),
v.minLength(3, 'Username must be at least 3 characters long.')
v.minLength(3, "Username must be at least 3 characters long.")
),
email: emailSchema,
password: passwordSchema,
@@ -47,9 +39,9 @@ const RegisterSchema = v.looseObject({
const showLoginFaliedToast = () => {
toast({
title: 'Login attempt failed',
description: 'Please check your credentials and try again',
variant: 'destructive',
title: t`Login attempt failed`,
description: t`Please check your credentials and try again`,
variant: "destructive",
})
}
@@ -90,7 +82,7 @@ export function UserAuthForm({
if (isFirstRun) {
// check that passwords match
if (password !== passwordConfirm) {
let msg = 'Passwords do not match'
let msg = "Passwords do not match"
setErrors({ passwordConfirm: msg })
return
}
@@ -100,17 +92,17 @@ export function UserAuthForm({
passwordConfirm: password,
})
await pb.admins.authWithPassword(email, password)
await pb.collection('users').create({
await pb.collection("users").create({
username,
email,
password,
passwordConfirm: password,
role: 'admin',
role: "admin",
verified: true,
})
await pb.collection('users').authWithPassword(email, password)
await pb.collection("users").authWithPassword(email, password)
} else {
await pb.collection('users').authWithPassword(email, password)
await pb.collection("users").authWithPassword(email, password)
}
$authenticated.set(true)
} catch (e) {
@@ -127,7 +119,7 @@ export function UserAuthForm({
}
return (
<div className={cn('grid gap-6', className)} {...props}>
<div className={cn("grid gap-6", className)} {...props}>
{authMethods.emailPassword && (
<>
<form onSubmit={handleSubmit} onChange={() => setErrors({})}>
@@ -136,59 +128,57 @@ export function UserAuthForm({
<div className="grid gap-1 relative">
<UserIcon className="absolute left-3 top-3 h-4 w-4 text-muted-foreground" />
<Label className="sr-only" htmlFor="username">
Username
<Trans>Username</Trans>
</Label>
<Input
autoFocus={true}
id="username"
name="username"
required
placeholder="username"
placeholder={t`username`}
type="username"
autoCapitalize="none"
autoComplete="username"
autoCorrect="off"
disabled={isLoading || isOauthLoading}
className="pl-9"
className="ps-9"
/>
{errors?.username && (
<p className="px-1 text-xs text-red-600">{errors.username}</p>
)}
{errors?.username && <p className="px-1 text-xs text-red-600">{errors.username}</p>}
</div>
)}
<div className="grid gap-1 relative">
<MailIcon className="absolute left-3 top-3 h-4 w-4 text-muted-foreground" />
<Label className="sr-only" htmlFor="email">
Email
<Trans>Email</Trans>
</Label>
<Input
id="email"
name="email"
required
placeholder={isFirstRun ? 'email' : 'name@example.com'}
placeholder={isFirstRun ? t`email` : "name@example.com"}
type="email"
autoCapitalize="none"
autoComplete="email"
autoCorrect="off"
disabled={isLoading || isOauthLoading}
className="pl-9"
className="ps-9"
/>
{errors?.email && <p className="px-1 text-xs text-red-600">{errors.email}</p>}
</div>
<div className="grid gap-1 relative">
<LockIcon className="absolute left-3 top-3 h-4 w-4 text-muted-foreground" />
<Label className="sr-only" htmlFor="pass">
Password
<Trans>Password</Trans>
</Label>
<Input
id="pass"
name="password"
placeholder="password"
placeholder={t`Password`}
required
type="password"
autoComplete="current-password"
disabled={isLoading || isOauthLoading}
className="pl-9"
className="ps-9 lowercase"
/>
{errors?.password && <p className="px-1 text-xs text-red-600">{errors.password}</p>}
</div>
@@ -196,21 +186,19 @@ export function UserAuthForm({
<div className="grid gap-1 relative">
<LockIcon className="absolute left-3 top-3 h-4 w-4 text-muted-foreground" />
<Label className="sr-only" htmlFor="pass2">
Confirm password
<Trans>Confirm password</Trans>
</Label>
<Input
id="pass2"
name="passwordConfirm"
placeholder="confirm password"
placeholder={t`Confirm password`}
required
type="password"
autoComplete="current-password"
disabled={isLoading || isOauthLoading}
className="pl-9"
className="ps-9 lowercase"
/>
{errors?.passwordConfirm && (
<p className="px-1 text-xs text-red-600">{errors.passwordConfirm}</p>
)}
{errors?.passwordConfirm && <p className="px-1 text-xs text-red-600">{errors.passwordConfirm}</p>}
</div>
)}
<div className="sr-only">
@@ -220,11 +208,11 @@ export function UserAuthForm({
</div>
<button className={cn(buttonVariants())} disabled={isLoading}>
{isLoading ? (
<LoaderCircle className="mr-2 h-4 w-4 animate-spin" />
<LoaderCircle className="me-2 h-4 w-4 animate-spin" />
) : (
<LogInIcon className="mr-2 h-4 w-4" />
<LogInIcon className="me-2 h-4 w-4" />
)}
{isFirstRun ? 'Create account' : 'Sign in'}
{isFirstRun ? t`Create account` : t`Sign in`}
</button>
</div>
</form>
@@ -235,7 +223,9 @@ export function UserAuthForm({
<span className="w-full border-t" />
</div>
<div className="relative flex justify-center text-xs uppercase">
<span className="bg-background px-2 text-muted-foreground">Or continue with</span>
<span className="bg-background px-2 text-muted-foreground">
<Trans>Or continue with</Trans>
</span>
</div>
</div>
)}
@@ -248,9 +238,9 @@ export function UserAuthForm({
<button
key={provider.name}
type="button"
className={cn(buttonVariants({ variant: 'outline' }), {
'justify-self-center': !authMethods.emailPassword,
'px-5': !authMethods.emailPassword,
className={cn(buttonVariants({ variant: "outline" }), {
"justify-self-center": !authMethods.emailPassword,
"px-5": !authMethods.emailPassword,
})}
onClick={() => {
setIsOauthLoading(true)
@@ -263,9 +253,9 @@ export function UserAuthForm({
if (!authWindow) {
setIsOauthLoading(false)
toast({
title: 'Error',
description: 'Please enable pop-ups for this site',
variant: 'destructive',
title: t`Error`,
description: t`Please enable pop-ups for this site`,
variant: "destructive",
})
return
}
@@ -273,7 +263,7 @@ export function UserAuthForm({
authWindow.location.href = url
}
}
pb.collection('users')
pb.collection("users")
.authWithOAuth2(oAuthOpts)
.then(() => {
$authenticated.set(pb.authStore.isValid)
@@ -286,14 +276,14 @@ export function UserAuthForm({
disabled={isLoading || isOauthLoading}
>
{isOauthLoading ? (
<LoaderCircle className="mr-2 h-4 w-4 animate-spin" />
<LoaderCircle className="me-2 h-4 w-4 animate-spin" />
) : (
<img
className="mr-2 h-4 w-4 dark:invert"
className="me-2 h-4 w-4 dark:invert"
src={`/static/${provider.name}.svg`}
alt=""
onError={(e) => {
e.currentTarget.src = '/static/lock.svg'
e.currentTarget.src = "/static/lock.svg"
}}
/>
)}
@@ -307,26 +297,32 @@ export function UserAuthForm({
// only show GitHub button / dialog during onboarding
<Dialog>
<DialogTrigger asChild>
<button type="button" className={cn(buttonVariants({ variant: 'outline' }))}>
<img className="mr-2 h-4 w-4 dark:invert" src="/static/github.svg" alt="" />
<button type="button" className={cn(buttonVariants({ variant: "outline" }))}>
<img className="me-2 h-4 w-4 dark:invert" src="/static/github.svg" alt="" />
<span className="translate-y-[1px]">GitHub</span>
</button>
</DialogTrigger>
<DialogContent style={{ maxWidth: 440, width: '90%' }}>
<DialogContent style={{ maxWidth: 440, width: "90%" }}>
<DialogHeader>
<DialogTitle>OAuth 2 / OIDC support</DialogTitle>
<DialogTitle>
<Trans>OAuth 2 / OIDC support</Trans>
</DialogTitle>
</DialogHeader>
<div className="text-primary/70 text-[0.95em] contents">
<p>Beszel supports OpenID Connect and many OAuth2 authentication providers.</p>
<p>
Please view the{' '}
<a
href="https://github.com/henrygd/beszel/blob/main/readme.md#oauth--oidc-integration"
className={cn(buttonVariants({ variant: 'link' }), 'p-0 h-auto')}
>
GitHub README
</a>{' '}
for instructions.
<Trans>Beszel supports OpenID Connect and many OAuth2 authentication providers.</Trans>
</p>
<p>
<Trans>
Please see{" "}
<a
href="https://github.com/henrygd/beszel/blob/main/readme.md#oauth--oidc-integration"
className={cn(buttonVariants({ variant: "link" }), "p-0 h-auto")}
>
the documentation
</a>{" "}
for instructions.
</Trans>
</p>
</div>
</DialogContent>
@@ -338,7 +334,7 @@ export function UserAuthForm({
href="/forgot-password"
className="text-sm mx-auto hover:text-brand underline underline-offset-4 opacity-70 hover:opacity-100 transition-opacity"
>
Forgot password?
<Trans>Forgot password?</Trans>
</Link>
)}
</div>

View File

@@ -1,25 +1,26 @@
import { LoaderCircle, MailIcon, SendHorizonalIcon } from 'lucide-react'
import { Input } from '../ui/input'
import { Label } from '../ui/label'
import { useCallback, useState } from 'react'
import { toast } from '../ui/use-toast'
import { buttonVariants } from '../ui/button'
import { cn } from '@/lib/utils'
import { pb } from '@/lib/stores'
import { Dialog, DialogHeader } from '../ui/dialog'
import { DialogContent, DialogTrigger, DialogTitle } from '../ui/dialog'
import { LoaderCircle, MailIcon, SendHorizonalIcon } from "lucide-react"
import { Input } from "../ui/input"
import { Label } from "../ui/label"
import { useCallback, useState } from "react"
import { toast } from "../ui/use-toast"
import { buttonVariants } from "../ui/button"
import { cn } from "@/lib/utils"
import { pb } from "@/lib/stores"
import { Dialog, DialogHeader } from "../ui/dialog"
import { DialogContent, DialogTrigger, DialogTitle } from "../ui/dialog"
import { t, Trans } from "@lingui/macro"
const showLoginFaliedToast = () => {
toast({
title: 'Login attempt failed',
description: 'Please check your credentials and try again',
variant: 'destructive',
title: t`Login attempt failed`,
description: t`Please check your credentials and try again`,
variant: "destructive",
})
}
export default function ForgotPassword() {
const [isLoading, setIsLoading] = useState<boolean>(false)
const [email, setEmail] = useState('')
const [email, setEmail] = useState("")
const handleSubmit = useCallback(
async (e: React.FormEvent<HTMLFormElement>) => {
@@ -27,16 +28,16 @@ export default function ForgotPassword() {
setIsLoading(true)
try {
// console.log(email)
await pb.collection('users').requestPasswordReset(email)
await pb.collection("users").requestPasswordReset(email)
toast({
title: 'Password reset request received',
description: `Check ${email} for a reset link.`,
title: t`Password reset request received`,
description: t`Check ${email} for a reset link.`,
})
} catch (e) {
showLoginFaliedToast()
} finally {
setIsLoading(false)
setEmail('')
setEmail("")
}
},
[email]
@@ -49,7 +50,7 @@ export default function ForgotPassword() {
<div className="grid gap-1 relative">
<MailIcon className="absolute left-3 top-3 h-4 w-4 text-muted-foreground" />
<Label className="sr-only" htmlFor="email">
Email
<Trans>Email</Trans>
</Label>
<Input
value={email}
@@ -63,37 +64,40 @@ export default function ForgotPassword() {
autoComplete="email"
autoCorrect="off"
disabled={isLoading}
className="pl-9"
className="ps-9"
/>
</div>
<button className={cn(buttonVariants())} disabled={isLoading}>
{isLoading ? (
<LoaderCircle className="mr-2 h-4 w-4 animate-spin" />
<LoaderCircle className="me-2 h-4 w-4 animate-spin" />
) : (
<SendHorizonalIcon className="mr-2 h-4 w-4" />
<SendHorizonalIcon className="me-2 h-4 w-4" />
)}
Reset password
<Trans>Reset Password</Trans>
</button>
</div>
</form>
<Dialog>
<DialogTrigger asChild>
<button className="text-sm mx-auto hover:text-brand underline underline-offset-4 opacity-70 hover:opacity-100 transition-opacity">
Command line instructions
<Trans>Command line instructions</Trans>
</button>
</DialogTrigger>
<DialogContent className="max-w-[33em]">
<DialogHeader>
<DialogTitle>Command line instructions</DialogTitle>
<DialogTitle>
<Trans>Command line instructions</Trans>
</DialogTitle>
</DialogHeader>
<p className="text-primary/70 text-[0.95em] leading-relaxed">
If you've lost the password to your admin account, you may reset it using the following
command.
<Trans>
If you've lost the password to your admin account, you may reset it using the following command.
</Trans>
</p>
<p className="text-primary/70 text-[0.95em] leading-relaxed">
Then log into the backend and reset your user account password in the users table.
<Trans>Then log into the backend and reset your user account password in the users table.</Trans>
</p>
<code className="bg-muted rounded-sm py-0.5 px-2.5 mr-auto text-sm">
<code className="bg-muted rounded-sm py-0.5 px-2.5 me-auto text-sm">
beszel admin update youremail@example.com newpassword
</code>
</DialogContent>

View File

@@ -1,11 +1,12 @@
import { UserAuthForm } from '@/components/login/auth-form'
import { Logo } from '../logo'
import { useEffect, useMemo, useState } from 'react'
import { pb } from '@/lib/stores'
import { useStore } from '@nanostores/react'
import ForgotPassword from './forgot-pass-form'
import { $router } from '../router'
import { AuthMethodsList } from 'pocketbase'
import { UserAuthForm } from "@/components/login/auth-form"
import { Logo } from "../logo"
import { useEffect, useMemo, useState } from "react"
import { pb } from "@/lib/stores"
import { useStore } from "@nanostores/react"
import ForgotPassword from "./forgot-pass-form"
import { $router } from "../router"
import { AuthMethodsList } from "pocketbase"
import { t } from "@lingui/macro"
export default function () {
const page = useStore($router)
@@ -13,15 +14,15 @@ export default function () {
const [authMethods, setAuthMethods] = useState<AuthMethodsList>()
useEffect(() => {
document.title = 'Login / Beszel'
document.title = t`Login` + " / Beszel"
pb.send('/api/beszel/first-run', {}).then(({ firstRun }) => {
pb.send("/api/beszel/first-run", {}).then(({ firstRun }) => {
setFirstRun(firstRun)
})
}, [])
useEffect(() => {
pb.collection('users')
pb.collection("users")
.listAuthMethods()
.then((methods) => {
setAuthMethods(methods)
@@ -30,11 +31,11 @@ export default function () {
const subtitle = useMemo(() => {
if (isFirstRun) {
return 'Please create an admin account'
} else if (page?.path === '/forgot-password') {
return 'Enter email address to reset password'
return t`Please create an admin account`
} else if (page?.path === "/forgot-password") {
return t`Enter email address to reset password`
} else {
return 'Please sign in to your account'
return t`Please sign in to your account`
}
}, [isFirstRun, page])
@@ -44,7 +45,7 @@ export default function () {
return (
<div className="min-h-svh grid items-center py-12">
<div className="grid gap-5 w-full px-4 mx-auto" style={{ maxWidth: '22em' }}>
<div className="grid gap-5 w-full px-4 mx-auto" style={{ maxWidth: "22em" }}>
<div className="text-center">
<h1 className="mb-3">
<Logo className="h-7 fill-foreground mx-auto" />
@@ -52,7 +53,7 @@ export default function () {
</h1>
<p className="text-sm text-muted-foreground">{subtitle}</p>
</div>
{page?.path === '/forgot-password' ? (
{page?.path === "/forgot-password" ? (
<ForgotPassword />
) : (
<UserAuthForm isFirstRun={isFirstRun} authMethods={authMethods} />

View File

@@ -2,7 +2,16 @@ export function Logo({ className }: { className?: string }) {
return (
// Righteous
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 285 75" className={className}>
<path d="M146.4 73.1h-30.5V59.8h30.5a3.2 3.2 0 0 0 2.3-1 3.2 3.2 0 0 0 1-2.3q0-.8-.3-1.3a1.5 1.5 0 0 0-.7-.6 4.7 4.7 0 0 0-1-.3l-1.3-.1h-13.9q-3.4 0-6.5-1.3-3-1.3-5.2-3.6a16.9 16.9 0 0 1-3.6-5.3 16.3 16.3 0 0 1-1.3-6.5 16.4 16.4 0 0 1 1.3-6.4q1.3-3.1 3.6-5.4 2.2-2.2 5.2-3.5a16.3 16.3 0 0 1 6.5-1.3h27v13.3h-27a3.2 3.2 0 0 0-2.3 1 3.2 3.2 0 0 0-1 2.3 3.3 3.3 0 0 0 1 2.4 3.3 3.3 0 0 0 1.2.8 3.2 3.2 0 0 0 1.1.2h13.9a18.1 18.1 0 0 1 6 1 17.3 17.3 0 0 1 .4.2q3 1.1 5.3 3.2a15.1 15.1 0 0 1 3.6 4.9 14.7 14.7 0 0 1 1.3 5.4 17.2 17.2 0 0 1 0 .9 16 16 0 0 1-1 5.8 15.4 15.4 0 0 1-.3.7 17.3 17.3 0 0 1-3.6 5.2 16.4 16.4 0 0 1-5.3 3.6 16.2 16.2 0 0 1-6.4 1.3Zm64.5-13.3v13.3h-43.6l22-39h-22V21h43.6l-22 39h22ZM35 73.1H0v-70h35q4.4 0 8.2 1.6a21.4 21.4 0 0 1 6.6 4.6q2.9 2.8 4.5 6.6 1.7 3.8 1.7 8.2a15.4 15.4 0 0 1-.3 3.2 17.6 17.6 0 0 1-.2.8 19.4 19.4 0 0 1-1.5 4 17 17 0 0 1-2.4 3.4 13.5 13.5 0 0 1-2.6 2.3 12.5 12.5 0 0 1-.4.3q1.7 1 3 2.5 1.4 1.6 2.4 3.5a18.3 18.3 0 0 1 1.5 4A17.4 17.4 0 0 1 56 51a15.3 15.3 0 0 1 0 1.1q0 4.3-1.7 8.2a21.4 21.4 0 0 1-4.5 6.6q-2.8 2.9-6.6 4.5-3.8 1.7-8.2 1.7Zm76-43L86 60.4l1.5.3a16.7 16.7 0 0 0 1.6 0q2 0 3.8-.4 1.8-.6 3.4-1.6 1.6-1 2.8-2.4a12.8 12.8 0 0 0 2-3.2l9.8 9.8q-1.9 2.6-4.3 4.7a27 27 0 0 1-5.2 3.6 26.1 26.1 0 0 1-6 2.2 26.8 26.8 0 0 1-6.3.8 26.4 26.4 0 0 1-10.4-2 26.2 26.2 0 0 1-8.5-5.8 26.7 26.7 0 0 1-5.5-8.3 30.4 30.4 0 0 1-.2-.4q-2.1-5-2.1-11.1a31.9 31.9 0 0 1 .7-7 27 27 0 0 1 1.4-4.3 27 27 0 0 1 3.8-6.6 24.5 24.5 0 0 1 2-2.2 26 26 0 0 1 8.4-5.6 27 27 0 0 1 10.4-2 26.3 26.3 0 0 1 6.4.8 26.9 26.9 0 0 1 6 2.2q2.7 1.5 5.2 3.6 2.4 2.1 4.3 4.8Zm152.3 0-25 30.2 1.5.3a16.7 16.7 0 0 0 1.6 0q2 0 3.8-.4 1.8-.6 3.4-1.6 1.5-1 2.8-2.4a12.8 12.8 0 0 0 2-3.2l9.8 9.8q-1.9 2.6-4.3 4.7a27 27 0 0 1-5.2 3.6 26.1 26.1 0 0 1-6 2.2 26.8 26.8 0 0 1-6.3.8 26.4 26.4 0 0 1-10.4-2 26.2 26.2 0 0 1-8.5-5.8A26.7 26.7 0 0 1 217 58a30.4 30.4 0 0 1-.2-.4q-2.1-5-2.1-11.1a31.9 31.9 0 0 1 .7-7 27 27 0 0 1 1.4-4.3 27 27 0 0 1 3.8-6.6 24.5 24.5 0 0 1 2-2.2 26 26 0 0 1 8.4-5.6 27 27 0 0 1 10.4-2 26.3 26.3 0 0 1 6.4.8 26.9 26.9 0 0 1 6 2.2q2.7 1.5 5.2 3.6 2.4 2.1 4.3 4.8ZM283.4 0v73.1H270V0h13.4ZM14 17v14.1h21a7 7 0 0 0 2.3-.4 6.6 6.6 0 0 0 .4-.1Q39 30 40 29a6.9 6.9 0 0 0 1.5-2.3q.5-1.3.5-2.7a7 7 0 0 0-.4-2.3 6.6 6.6 0 0 0-.1-.5q-.6-1.2-1.5-2.2a7 7 0 0 0-2.3-1.5 6.9 6.9 0 0 0-2.5-.5 7.9 7.9 0 0 0-.2 0H14Zm0 28.1v14h21a7 7 0 0 0 2.3-.4 6.6 6.6 0 0 0 .4-.2Q39 58 40 57.1a7 7 0 0 0 1.5-2.3 6.9 6.9 0 0 0 .5-2.5 7.9 7.9 0 0 0 0-.2 7 7 0 0 0-.4-2.3 6.6 6.6 0 0 0-.1-.4Q40.9 48 40 47a7 7 0 0 0-2.3-1.4 6.9 6.9 0 0 0-2.5-.6 7.9 7.9 0 0 0-.2 0H14Zm63.3 8.3 15.5-20.6a8 8 0 0 0-1.4-.4 7 7 0 0 0-.4 0 17.2 17.2 0 0 0-1.6-.1 19.2 19.2 0 0 0-.3 0 13.3 13.3 0 0 0-5.1 1q-2.5 1-4.2 2.8a13.1 13.1 0 0 0-2.5 3.6 15.5 15.5 0 0 0-.3.9 14.7 14.7 0 0 0-1 3.5 18.7 18.7 0 0 0 0 2.4 17.6 17.6 0 0 0 0 .7v.8a29.4 29.4 0 0 0 0 .1 19.2 19.2 0 0 0 .2 2 20.2 20.2 0 0 0 .4 1.6 18.6 18.6 0 0 0 0 .2 7.5 7.5 0 0 0 .4.9 6 6 0 0 0 .3.6Zm152.3 0L245 32.8a8 8 0 0 0-1.4-.4 7 7 0 0 0-.4 0 17.2 17.2 0 0 0-1.6-.1 19.2 19.2 0 0 0-.3 0 13.3 13.3 0 0 0-5.1 1q-2.5 1-4.2 2.8a13.1 13.1 0 0 0-2.5 3.6 15.5 15.5 0 0 0-.4.9 14.7 14.7 0 0 0-.8 3.5 18.7 18.7 0 0 0-.2 2.4 17.6 17.6 0 0 0 0 .7v.8a29.4 29.4 0 0 0 .1.1 19.2 19.2 0 0 0 .2 2 20.2 20.2 0 0 0 .4 1.6 18.6 18.6 0 0 0 0 .2 7.5 7.5 0 0 0 .4.9 6 6 0 0 0 .3.6Z" />
{/* <defs>
<linearGradient id="gradient" x1="0%" y1="20%" x2="100%" y2="120%">
<stop offset="0%" style={{ stopColor: "#747bff" }} />
<stop offset="100%" style={{ stopColor: "#24eb5c" }} />
</linearGradient>
</defs> */}
<path
// fill="url(#gradient)"
d="M146.4 73.1h-30.5V59.8h30.5a3.2 3.2 0 0 0 2.3-1 3.2 3.2 0 0 0 1-2.3q0-.8-.3-1.3a1.5 1.5 0 0 0-.7-.6 4.7 4.7 0 0 0-1-.3l-1.3-.1h-13.9q-3.4 0-6.5-1.3-3-1.3-5.2-3.6a16.9 16.9 0 0 1-3.6-5.3 16.3 16.3 0 0 1-1.3-6.5 16.4 16.4 0 0 1 1.3-6.4q1.3-3.1 3.6-5.4 2.2-2.2 5.2-3.5a16.3 16.3 0 0 1 6.5-1.3h27v13.3h-27a3.2 3.2 0 0 0-2.3 1 3.2 3.2 0 0 0-1 2.3 3.3 3.3 0 0 0 1 2.4 3.3 3.3 0 0 0 1.2.8 3.2 3.2 0 0 0 1.1.2h13.9a18.1 18.1 0 0 1 6 1 17.3 17.3 0 0 1 .4.2q3 1.1 5.3 3.2a15.1 15.1 0 0 1 3.6 4.9 14.7 14.7 0 0 1 1.3 5.4 17.2 17.2 0 0 1 0 .9 16 16 0 0 1-1 5.8 15.4 15.4 0 0 1-.3.7 17.3 17.3 0 0 1-3.6 5.2 16.4 16.4 0 0 1-5.3 3.6 16.2 16.2 0 0 1-6.4 1.3Zm64.5-13.3v13.3h-43.6l22-39h-22V21h43.6l-22 39h22ZM35 73.1H0v-70h35q4.4 0 8.2 1.6a21.4 21.4 0 0 1 6.6 4.6q2.9 2.8 4.5 6.6 1.7 3.8 1.7 8.2a15.4 15.4 0 0 1-.3 3.2 17.6 17.6 0 0 1-.2.8 19.4 19.4 0 0 1-1.5 4 17 17 0 0 1-2.4 3.4 13.5 13.5 0 0 1-2.6 2.3 12.5 12.5 0 0 1-.4.3q1.7 1 3 2.5 1.4 1.6 2.4 3.5a18.3 18.3 0 0 1 1.5 4A17.4 17.4 0 0 1 56 51a15.3 15.3 0 0 1 0 1.1q0 4.3-1.7 8.2a21.4 21.4 0 0 1-4.5 6.6q-2.8 2.9-6.6 4.5-3.8 1.7-8.2 1.7Zm76-43L86 60.4l1.5.3a16.7 16.7 0 0 0 1.6 0q2 0 3.8-.4 1.8-.6 3.4-1.6 1.6-1 2.8-2.4a12.8 12.8 0 0 0 2-3.2l9.8 9.8q-1.9 2.6-4.3 4.7a27 27 0 0 1-5.2 3.6 26.1 26.1 0 0 1-6 2.2 26.8 26.8 0 0 1-6.3.8 26.4 26.4 0 0 1-10.4-2 26.2 26.2 0 0 1-8.5-5.8 26.7 26.7 0 0 1-5.5-8.3 30.4 30.4 0 0 1-.2-.4q-2.1-5-2.1-11.1a31.9 31.9 0 0 1 .7-7 27 27 0 0 1 1.4-4.3 27 27 0 0 1 3.8-6.6 24.5 24.5 0 0 1 2-2.2 26 26 0 0 1 8.4-5.6 27 27 0 0 1 10.4-2 26.3 26.3 0 0 1 6.4.8 26.9 26.9 0 0 1 6 2.2q2.7 1.5 5.2 3.6 2.4 2.1 4.3 4.8Zm152.3 0-25 30.2 1.5.3a16.7 16.7 0 0 0 1.6 0q2 0 3.8-.4 1.8-.6 3.4-1.6 1.5-1 2.8-2.4a12.8 12.8 0 0 0 2-3.2l9.8 9.8q-1.9 2.6-4.3 4.7a27 27 0 0 1-5.2 3.6 26.1 26.1 0 0 1-6 2.2 26.8 26.8 0 0 1-6.3.8 26.4 26.4 0 0 1-10.4-2 26.2 26.2 0 0 1-8.5-5.8A26.7 26.7 0 0 1 217 58a30.4 30.4 0 0 1-.2-.4q-2.1-5-2.1-11.1a31.9 31.9 0 0 1 .7-7 27 27 0 0 1 1.4-4.3 27 27 0 0 1 3.8-6.6 24.5 24.5 0 0 1 2-2.2 26 26 0 0 1 8.4-5.6 27 27 0 0 1 10.4-2 26.3 26.3 0 0 1 6.4.8 26.9 26.9 0 0 1 6 2.2q2.7 1.5 5.2 3.6 2.4 2.1 4.3 4.8ZM283.4 0v73.1H270V0h13.4ZM14 17v14.1h21a7 7 0 0 0 2.3-.4 6.6 6.6 0 0 0 .4-.1Q39 30 40 29a6.9 6.9 0 0 0 1.5-2.3q.5-1.3.5-2.7a7 7 0 0 0-.4-2.3 6.6 6.6 0 0 0-.1-.5q-.6-1.2-1.5-2.2a7 7 0 0 0-2.3-1.5 6.9 6.9 0 0 0-2.5-.5 7.9 7.9 0 0 0-.2 0H14Zm0 28.1v14h21a7 7 0 0 0 2.3-.4 6.6 6.6 0 0 0 .4-.2Q39 58 40 57.1a7 7 0 0 0 1.5-2.3 6.9 6.9 0 0 0 .5-2.5 7.9 7.9 0 0 0 0-.2 7 7 0 0 0-.4-2.3 6.6 6.6 0 0 0-.1-.4Q40.9 48 40 47a7 7 0 0 0-2.3-1.4 6.9 6.9 0 0 0-2.5-.6 7.9 7.9 0 0 0-.2 0H14Zm63.3 8.3 15.5-20.6a8 8 0 0 0-1.4-.4 7 7 0 0 0-.4 0 17.2 17.2 0 0 0-1.6-.1 19.2 19.2 0 0 0-.3 0 13.3 13.3 0 0 0-5.1 1q-2.5 1-4.2 2.8a13.1 13.1 0 0 0-2.5 3.6 15.5 15.5 0 0 0-.3.9 14.7 14.7 0 0 0-1 3.5 18.7 18.7 0 0 0 0 2.4 17.6 17.6 0 0 0 0 .7v.8a29.4 29.4 0 0 0 0 .1 19.2 19.2 0 0 0 .2 2 20.2 20.2 0 0 0 .4 1.6 18.6 18.6 0 0 0 0 .2 7.5 7.5 0 0 0 .4.9 6 6 0 0 0 .3.6Zm152.3 0L245 32.8a8 8 0 0 0-1.4-.4 7 7 0 0 0-.4 0 17.2 17.2 0 0 0-1.6-.1 19.2 19.2 0 0 0-.3 0 13.3 13.3 0 0 0-5.1 1q-2.5 1-4.2 2.8a13.1 13.1 0 0 0-2.5 3.6 15.5 15.5 0 0 0-.4.9 14.7 14.7 0 0 0-.8 3.5 18.7 18.7 0 0 0-.2 2.4 17.6 17.6 0 0 0 0 .7v.8a29.4 29.4 0 0 0 .1.1 19.2 19.2 0 0 0 .2 2 20.2 20.2 0 0 0 .4 1.6 18.6 18.6 0 0 0 0 .2 7.5 7.5 0 0 0 .4.9 6 6 0 0 0 .3.6Z"
/>
</svg>
)
}

View File

@@ -1,39 +1,54 @@
import { LaptopIcon, MoonStarIcon, SunIcon } from 'lucide-react'
import { LaptopIcon, MoonStarIcon, SunIcon } from "lucide-react"
import { Button } from '@/components/ui/button'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import { useTheme } from '@/components/theme-provider'
import { Button } from "@/components/ui/button"
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
import { useTheme } from "@/components/theme-provider"
import { cn } from "@/lib/utils"
import { t, Trans } from "@lingui/macro"
export function ModeToggle() {
const { setTheme } = useTheme()
const { theme, setTheme } = useTheme()
const options = [
{
theme: "light",
Icon: SunIcon,
label: <Trans comment="Light theme">Light</Trans>,
},
{
theme: "dark",
Icon: MoonStarIcon,
label: <Trans comment="Dark theme">Dark</Trans>,
},
{
theme: "system",
Icon: LaptopIcon,
label: <Trans comment="System theme">System</Trans>,
},
]
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant={'ghost'} size="icon">
<Button variant={"ghost"} size="icon" aria-label={t`Toggle theme`}>
<SunIcon className="h-[1.2rem] w-[1.2rem] dark:opacity-0" />
<MoonStarIcon className="absolute h-[1.2rem] w-[1.2rem] opacity-0 dark:opacity-100" />
<span className="sr-only">Toggle theme</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem onClick={() => setTheme('light')}>
<SunIcon className="mr-2.5 h-4 w-4" />
Light
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme('dark')}>
<MoonStarIcon className="mr-2.5 h-4 w-4" />
Dark
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme('system')}>
<LaptopIcon className="mr-2.5 h-4 w-4" />
System
</DropdownMenuItem>
{options.map((opt) => {
const selected = opt.theme === theme
return (
<DropdownMenuItem
key={opt.theme}
className={cn("px-2.5", selected ? "font-semibold" : "")}
onClick={() => setTheme(opt.theme as "dark" | "light" | "system")}
>
<opt.Icon className={cn("me-2 h-4 w-4 opacity-80", selected && "opacity-100")} />
{opt.label}
</DropdownMenuItem>
)
})}
</DropdownMenuContent>
</DropdownMenu>
)

View File

@@ -0,0 +1,154 @@
import { useState, lazy, Suspense } from "react"
import { Button, buttonVariants } from "@/components/ui/button"
import {
DatabaseBackupIcon,
LockKeyholeIcon,
LogOutIcon,
LogsIcon,
SearchIcon,
ServerIcon,
SettingsIcon,
UserIcon,
UsersIcon,
} from "lucide-react"
import { Link } from "./router"
import { LangToggle } from "./lang-toggle"
import { ModeToggle } from "./mode-toggle"
import { Logo } from "./logo"
import { pb } from "@/lib/stores"
import { cn, isReadOnlyUser, isAdmin } from "@/lib/utils"
import {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuGroup,
DropdownMenuItem,
} from "@/components/ui/dropdown-menu"
import { AddSystemButton } from "./add-system"
import { Trans } from "@lingui/macro"
const CommandPalette = lazy(() => import("./command-palette"))
const isMac = navigator.platform.toUpperCase().indexOf("MAC") >= 0
export default function Navbar() {
return (
<div className="flex items-center h-14 md:h-16 bg-card px-4 pe-3 sm:px-6 border bt-0 rounded-md my-4">
<Link href="/" aria-label="Home" className="p-2 ps-0 me-3">
<Logo className="h-[1.1rem] md:h-5 fill-foreground" />
</Link>
<SearchButton />
<div className="flex items-center ms-auto">
<LangToggle />
<ModeToggle />
<Link
href="/settings/general"
aria-label="Settings"
className={cn("", buttonVariants({ variant: "ghost", size: "icon" }))}
>
<SettingsIcon className="h-[1.2rem] w-[1.2rem]" />
</Link>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button aria-label="User Actions" className={cn("", buttonVariants({ variant: "ghost", size: "icon" }))}>
<UserIcon className="h-[1.2rem] w-[1.2rem]" />
</button>
</DropdownMenuTrigger>
<DropdownMenuContent align={isReadOnlyUser() ? "end" : "center"} className="min-w-44">
<DropdownMenuLabel>{pb.authStore.model?.email}</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
{isAdmin() && (
<>
<DropdownMenuItem asChild>
<a href="/_/" target="_blank">
<UsersIcon className="me-2.5 h-4 w-4" />
<span>
<Trans>Users</Trans>
</span>
</a>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<a href="/_/#/collections?collectionId=2hz5ncl8tizk5nx" target="_blank">
<ServerIcon className="me-2.5 h-4 w-4" />
<span>
<Trans>Systems</Trans>
</span>
</a>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<a href="/_/#/logs" target="_blank">
<LogsIcon className="me-2.5 h-4 w-4" />
<span>
<Trans>Logs</Trans>
</span>
</a>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<a href="/_/#/settings/backups" target="_blank">
<DatabaseBackupIcon className="me-2.5 h-4 w-4" />
<span>
<Trans>Backups</Trans>
</span>
</a>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<a href="/_/#/settings/auth-providers" target="_blank">
<LockKeyholeIcon className="me-2.5 h-4 w-4" />
<span>
<Trans>Auth Providers</Trans>
</span>
</a>
</DropdownMenuItem>
<DropdownMenuSeparator />
</>
)}
</DropdownMenuGroup>
<DropdownMenuItem onSelect={() => pb.authStore.clear()}>
<LogOutIcon className="me-2.5 h-4 w-4" />
<span>
<Trans>Log Out</Trans>
</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<AddSystemButton className="ms-2" />
</div>
</div>
)
}
function SearchButton() {
const [open, setOpen] = useState(false)
const Kbd = ({ children }: { children: React.ReactNode }) => (
<kbd className="pointer-events-none inline-flex h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground opacity-100">
{children}
</kbd>
)
return (
<>
<Button
variant="outline"
className="hidden md:block text-sm text-muted-foreground px-4"
onClick={() => setOpen(true)}
>
<span className="flex items-center">
<SearchIcon className="me-1.5 h-4 w-4" />
<Trans>Search</Trans>
<span className="flex items-center ms-3.5">
<Kbd>{isMac ? "⌘" : "Ctrl"}</Kbd>
<Kbd>K</Kbd>
</span>
</span>
</Button>
<Suspense>
<CommandPalette open={open} setOpen={setOpen} />
</Suspense>
</>
)
}

View File

@@ -1,10 +1,11 @@
import { createRouter } from '@nanostores/router'
import { createRouter } from "@nanostores/router"
export const $router = createRouter(
{
home: '/',
server: '/system/:name',
settings: '/settings/:name?',
home: "/",
server: "/system/:name",
settings: "/settings/:name?",
forgot_password: "/forgot-password",
},
{ links: false }
)

View File

@@ -1,20 +1,20 @@
import { Suspense, lazy, useEffect, useMemo, useState } from 'react'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../ui/card'
import { $alerts, $hubVersion, $systems, pb } from '@/lib/stores'
import { useStore } from '@nanostores/react'
import { GithubIcon } from 'lucide-react'
import { Separator } from '../ui/separator'
import { alertInfo, updateRecordList, updateSystemList } from '@/lib/utils'
import { AlertRecord, SystemRecord } from '@/types'
import { Input } from '../ui/input'
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
import { Link } from '../router'
import { Suspense, lazy, useEffect, useMemo } from "react"
import { Card, CardContent, CardHeader, CardTitle } from "../ui/card"
import { $alerts, $hubVersion, $systems, pb } from "@/lib/stores"
import { useStore } from "@nanostores/react"
import { GithubIcon } from "lucide-react"
import { Separator } from "../ui/separator"
import { alertInfo, updateRecordList, updateSystemList } from "@/lib/utils"
import { AlertRecord, SystemRecord } from "@/types"
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
import { Link } from "../router"
import { Plural, t, Trans } from "@lingui/macro"
const SystemsTable = lazy(() => import('../systems-table/systems-table'))
const SystemsTable = lazy(() => import("../systems-table/systems-table"))
export default function () {
export default function Home() {
const hubVersion = useStore($hubVersion)
const [filter, setFilter] = useState<string>()
const alerts = useStore($alerts)
const systems = useStore($systems)
@@ -32,21 +32,21 @@ export default function () {
}, [alerts])
useEffect(() => {
document.title = 'Dashboard / Beszel'
document.title = t`Dashboard` + " / Beszel"
// make sure we have the latest list of systems
updateSystemList()
// subscribe to real time updates for systems / alerts
pb.collection<SystemRecord>('systems').subscribe('*', (e) => {
pb.collection<SystemRecord>("systems").subscribe("*", (e) => {
updateRecordList(e, $systems)
})
// todo: add toast if new triggered alert comes in
pb.collection<AlertRecord>('alerts').subscribe('*', (e) => {
pb.collection<AlertRecord>("alerts").subscribe("*", (e) => {
updateRecordList(e, $alerts)
})
return () => {
pb.collection('systems').unsubscribe('*')
pb.collection("systems").unsubscribe("*")
// pb.collection('alerts').unsubscribe('*')
}
}, [])
@@ -58,7 +58,9 @@ export default function () {
<Card className="mb-4">
<CardHeader className="pb-4 px-2 sm:px-6 max-sm:pt-5 max-sm:pb-1">
<div className="px-2 sm:px-1">
<CardTitle>Active Alerts</CardTitle>
<CardTitle>
<Trans>Active Alerts</Trans>
</CardTitle>
</div>
</CardHeader>
<CardContent className="max-sm:p-2">
@@ -73,11 +75,13 @@ export default function () {
>
<info.icon className="h-4 w-4" />
<AlertTitle>
{alert.sysname} {info.name}
{alert.sysname} {info.name().toLowerCase().replace("cpu", "CPU")}
</AlertTitle>
<AlertDescription>
Exceeds {alert.value}
{info.unit} average in last {alert.min} min
<Trans>
Exceeds {alert.value}
{info.unit} in last <Plural value={alert.min} one="# minute" other="# minutes" />
</Trans>
</AlertDescription>
<Link
href={`/system/${encodeURIComponent(alert.sysname!)}`}
@@ -92,35 +96,12 @@ export default function () {
</CardContent>
</Card>
)}
<Card>
<CardHeader className="pb-5 px-2 sm:px-6 max-sm:pt-5 max-sm:pb-1">
<div className="grid md:flex gap-3 w-full items-end">
<div className="px-2 sm:px-1">
<CardTitle className="mb-2.5">All Systems</CardTitle>
<CardDescription>
Updated in real time. Press{' '}
<kbd className="pointer-events-none inline-flex h-5 select-none items-center gap-0.5 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground opacity-100">
<span className="text-xs"></span>K
</kbd>{' '}
to open the command palette.
</CardDescription>
</div>
<Input
placeholder="Filter..."
onChange={(e) => setFilter(e.target.value)}
className="w-full md:w-56 lg:w-80 ml-auto px-4"
/>
</div>
</CardHeader>
<CardContent className="max-sm:p-2">
<Suspense>
<SystemsTable filter={filter} />
</Suspense>
</CardContent>
</Card>
<Suspense>
<SystemsTable />
</Suspense>
{hubVersion && (
<div className="flex gap-1.5 justify-end items-center pr-3 sm:pr-6 mt-3.5 text-xs opacity-80">
<div className="flex gap-1.5 justify-end items-center pe-3 sm:pe-6 mt-3.5 text-xs opacity-80">
<a
href="https://github.com/henrygd/beszel"
target="_blank"

View File

@@ -0,0 +1,97 @@
import { isAdmin } from "@/lib/utils"
import { Separator } from "@/components/ui/separator"
import { Button } from "@/components/ui/button"
import { redirectPage } from "@nanostores/router"
import { $router } from "@/components/router"
import { AlertCircleIcon, FileSlidersIcon, LoaderCircleIcon } from "lucide-react"
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
import { pb } from "@/lib/stores"
import { useState } from "react"
import { Textarea } from "@/components/ui/textarea"
import { toast } from "@/components/ui/use-toast"
import clsx from "clsx"
import { Trans, t } from "@lingui/macro"
export default function ConfigYaml() {
const [configContent, setConfigContent] = useState<string>("")
const [isLoading, setIsLoading] = useState(false)
const ButtonIcon = isLoading ? LoaderCircleIcon : FileSlidersIcon
async function fetchConfig() {
try {
setIsLoading(true)
const { config } = await pb.send<{ config: string }>("/api/beszel/config-yaml", {})
setConfigContent(config)
} catch (error: any) {
toast({
title: t`Error`,
description: error.message,
variant: "destructive",
})
} finally {
setIsLoading(false)
}
}
if (!isAdmin()) {
redirectPage($router, "settings", { name: "general" })
}
return (
<div>
<div>
<h3 className="text-xl font-medium mb-2">
<Trans>YAML Configuration</Trans>
</h3>
<p className="text-sm text-muted-foreground leading-relaxed">
<Trans>Export your current systems configuration.</Trans>
</p>
</div>
<Separator className="my-4" />
<div className="space-y-2">
<div className="mb-4">
<p className="text-sm text-muted-foreground leading-relaxed my-1">
<Trans>
Systems may be managed in a <code className="bg-muted rounded-sm px-1 text-primary">config.yml</code> file
inside your data directory.
</Trans>
</p>
<p className="text-sm text-muted-foreground leading-relaxed">
<Trans>
On each restart, systems in the database will be updated to match the systems defined in the file.
</Trans>
</p>
<Alert className="my-4 border-destructive text-destructive w-auto table md:pe-6">
<AlertCircleIcon className="h-4 w-4 stroke-destructive" />
<AlertTitle>
<Trans>Caution - potential data loss</Trans>
</AlertTitle>
<AlertDescription>
<p>
<Trans>
Existing systems not defined in <code>config.yml</code> will be deleted. Please make regular backups.
</Trans>
</p>
</AlertDescription>
</Alert>
</div>
{configContent && (
<Textarea
dir="ltr"
autoFocus
defaultValue={configContent}
spellCheck="false"
rows={Math.min(25, configContent.split("\n").length)}
className="font-mono whitespace-pre"
/>
)}
</div>
<Separator className="my-5" />
<Button type="button" className="mt-2 flex items-center gap-1" onClick={fetchConfig} disabled={isLoading}>
<ButtonIcon className={clsx("h-4 w-4 me-0.5", isLoading && "animate-spin")} />
<Trans>Export configuration</Trans>
</Button>
</div>
)
}

View File

@@ -1,22 +1,21 @@
import { Button } from '@/components/ui/button'
import { Label } from '@/components/ui/label'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
import { chartTimeData } from '@/lib/utils'
import { Separator } from '@/components/ui/separator'
import { LoaderCircleIcon, SaveIcon } from 'lucide-react'
import { UserSettings } from '@/types'
import { saveSettings } from './layout'
import { useState } from 'react'
// import { Input } from '@/components/ui/input'
import { Button } from "@/components/ui/button"
import { Label } from "@/components/ui/label"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { chartTimeData } from "@/lib/utils"
import { Separator } from "@/components/ui/separator"
import { LanguagesIcon, LoaderCircleIcon, SaveIcon } from "lucide-react"
import { UserSettings } from "@/types"
import { saveSettings } from "./layout"
import { useState } from "react"
import { Trans } from "@lingui/macro"
import languages from "@/lib/languages"
import { dynamicActivate } from "@/lib/i18n"
import { useLingui } from "@lingui/react"
// import { setLang } from "@/lib/i18n"
export default function SettingsProfilePage({ userSettings }: { userSettings: UserSettings }) {
const [isLoading, setIsLoading] = useState(false)
const { i18n } = useLingui()
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault()
@@ -30,79 +29,81 @@ export default function SettingsProfilePage({ userSettings }: { userSettings: Us
return (
<div>
<div>
<h3 className="text-xl font-medium mb-2">General</h3>
<h3 className="text-xl font-medium mb-2">
<Trans>General</Trans>
</h3>
<p className="text-sm text-muted-foreground leading-relaxed">
Change general application options.
<Trans>Change general application options.</Trans>
</p>
</div>
<Separator className="my-4" />
<form onSubmit={handleSubmit} className="space-y-5">
{/* <Separator />
<div className="space-y-2">
<div className="mb-4">
<h3 className="mb-1 text-lg font-medium">Language</h3>
<h3 className="mb-1 text-lg font-medium flex items-center gap-2">
<LanguagesIcon className="h-4 w-4" />
<Trans>Language</Trans>
</h3>
<p className="text-sm text-muted-foreground leading-relaxed">
Internationalization will be added in a future release. Please see the{' '}
<a href="#" className="link" target="_blank">
discussion on GitHub
</a>{' '}
for more details.
<Trans>
Want to help us make our translations even better? Check out{" "}
<a href="https://crowdin.com/project/beszel" className="link" target="_blank" rel="noopener noreferrer">
Crowdin
</a>{" "}
for more details.
</Trans>
</p>
</div>
<Label className="block" htmlFor="lang">
Preferred language
<Trans>Preferred Language</Trans>
</Label>
<Select defaultValue="en">
<Select value={i18n.locale} onValueChange={(lang: string) => dynamicActivate(lang)}>
<SelectTrigger id="lang">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="en">English</SelectItem>
{languages.map((lang) => (
<SelectItem key={lang.lang} value={lang.lang}>
<span className="me-2.5">{lang.e}</span>
{lang.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div> */}
</div>
<Separator />
<div className="space-y-2">
<div className="mb-4">
<h3 className="mb-1 text-lg font-medium">Chart options</h3>
<h3 className="mb-1 text-lg font-medium">
<Trans>Chart options</Trans>
</h3>
<p className="text-sm text-muted-foreground leading-relaxed">
Adjust display options for charts.
<Trans>Adjust display options for charts.</Trans>
</p>
</div>
<Label className="block" htmlFor="chartTime">
Default time period
<Trans>Default time period</Trans>
</Label>
<Select
name="chartTime"
key={userSettings.chartTime}
defaultValue={userSettings.chartTime}
>
<Select name="chartTime" key={userSettings.chartTime} defaultValue={userSettings.chartTime}>
<SelectTrigger id="chartTime">
<SelectValue />
</SelectTrigger>
<SelectContent>
{Object.entries(chartTimeData).map(([value, { label }]) => (
<SelectItem key={label} value={value}>
{label}
<SelectItem key={value} value={value}>
{label()}
</SelectItem>
))}
</SelectContent>
</Select>
<p className="text-[0.8rem] text-muted-foreground">
Sets the default time range for charts when a system is viewed.
<Trans>Sets the default time range for charts when a system is viewed.</Trans>
</p>
</div>
<Separator />
<Button
type="submit"
className="flex items-center gap-1.5 disabled:opacity-100"
disabled={isLoading}
>
{isLoading ? (
<LoaderCircleIcon className="h-4 w-4 animate-spin" />
) : (
<SaveIcon className="h-4 w-4" />
)}
Save settings
<Button type="submit" className="flex items-center gap-1.5 disabled:opacity-100" disabled={isLoading}>
{isLoading ? <LoaderCircleIcon className="h-4 w-4 animate-spin" /> : <SaveIcon className="h-4 w-4" />}
<Trans>Save Settings</Trans>
</Button>
</form>
</div>

View File

@@ -1,38 +1,28 @@
import { useEffect } from 'react'
import { Separator } from '../../ui/separator'
import { SidebarNav } from './sidebar-nav.tsx'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card.tsx'
import { useStore } from '@nanostores/react'
import { $router } from '@/components/router.tsx'
import { redirectPage } from '@nanostores/router'
import { BellIcon, SettingsIcon } from 'lucide-react'
import { $userSettings, pb } from '@/lib/stores.ts'
import { toast } from '@/components/ui/use-toast.ts'
import { UserSettings } from '@/types.js'
import General from './general.tsx'
import Notifications from './notifications.tsx'
const sidebarNavItems = [
{
title: 'General',
href: '/settings/general',
icon: SettingsIcon,
},
{
title: 'Notifications',
href: '/settings/notifications',
icon: BellIcon,
},
]
import { useEffect } from "react"
import { Separator } from "../../ui/separator"
import { SidebarNav } from "./sidebar-nav.tsx"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card.tsx"
import { useStore } from "@nanostores/react"
import { $router } from "@/components/router.tsx"
import { redirectPage } from "@nanostores/router"
import { BellIcon, FileSlidersIcon, SettingsIcon } from "lucide-react"
import { $userSettings, pb } from "@/lib/stores.ts"
import { toast } from "@/components/ui/use-toast.ts"
import { UserSettings } from "@/types.js"
import General from "./general.tsx"
import Notifications from "./notifications.tsx"
import ConfigYaml from "./config-yaml.tsx"
import { Trans, t } from "@lingui/macro"
import { useLingui } from "@lingui/react"
export async function saveSettings(newSettings: Partial<UserSettings>) {
try {
// get fresh copy of settings
const req = await pb.collection('user_settings').getFirstListItem('', {
fields: 'id,settings',
const req = await pb.collection("user_settings").getFirstListItem("", {
fields: "id,settings",
})
// update user settings
const updatedSettings = await pb.collection('user_settings').update(req.id, {
const updatedSettings = await pb.collection("user_settings").update(req.id, {
settings: {
...req.settings,
...newSettings,
@@ -40,35 +30,60 @@ export async function saveSettings(newSettings: Partial<UserSettings>) {
})
$userSettings.set(updatedSettings.settings)
toast({
title: 'Settings saved',
description: 'Your user settings have been updated.',
title: t`Settings saved`,
description: t`Your user settings have been updated.`,
})
} catch (e) {
// console.error('update settings', e)
toast({
title: 'Failed to save settings',
description: 'Check logs for more details.',
variant: 'destructive',
title: t`Failed to save settings`,
description: t`Check logs for more details.`,
variant: "destructive",
})
}
}
export default function SettingsLayout() {
const { _ } = useLingui()
const sidebarNavItems = [
{
title: _(t({ message: `General`, comment: "Context: General settings" })),
href: "/settings/general",
icon: SettingsIcon,
},
{
title: t`Notifications`,
href: "/settings/notifications",
icon: BellIcon,
},
{
title: t`YAML Config`,
href: "/settings/config",
icon: FileSlidersIcon,
admin: true,
},
]
const page = useStore($router)
useEffect(() => {
document.title = 'Settings / Beszel'
document.title = t`Settings` + " / Beszel"
// redirect to account page if no page is specified
if (page?.path === '/settings') {
redirectPage($router, 'settings', { name: 'general' })
if (page?.path === "/settings") {
redirectPage($router, "settings", { name: "general" })
}
}, [])
return (
<Card className="pt-5 px-4 pb-8 sm:pt-6 sm:px-7">
<CardHeader className="p-0">
<CardTitle className="mb-1">Settings</CardTitle>
<CardDescription>Manage display and notification preferences.</CardDescription>
<CardTitle className="mb-1">
<Trans>Settings</Trans>
</CardTitle>
<CardDescription>
<Trans>Manage display and notification preferences.</Trans>
</CardDescription>
</CardHeader>
<CardContent className="p-0">
<Separator className="hidden md:block my-5" />
@@ -78,7 +93,7 @@ export default function SettingsLayout() {
</aside>
<div className="flex-1">
{/* @ts-ignore */}
<SettingsContent name={page?.params?.name ?? 'general'} />
<SettingsContent name={page?.params?.name ?? "general"} />
</div>
</div>
</CardContent>
@@ -90,9 +105,11 @@ function SettingsContent({ name }: { name: string }) {
const userSettings = useStore($userSettings)
switch (name) {
case 'general':
case "general":
return <General userSettings={userSettings} />
case 'notifications':
case "notifications":
return <Notifications userSettings={userSettings} />
case "config":
return <ConfigYaml />
}
}

View File

@@ -1,17 +1,18 @@
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { pb } from '@/lib/stores'
import { Separator } from '@/components/ui/separator'
import { Card } from '@/components/ui/card'
import { BellIcon, LoaderCircleIcon, PlusIcon, SaveIcon, Trash2Icon } from 'lucide-react'
import { ChangeEventHandler, useEffect, useState } from 'react'
import { toast } from '@/components/ui/use-toast'
import { InputTags } from '@/components/ui/input-tags'
import { UserSettings } from '@/types'
import { saveSettings } from './layout'
import * as v from 'valibot'
import { isAdmin } from '@/lib/utils'
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { pb } from "@/lib/stores"
import { Separator } from "@/components/ui/separator"
import { Card } from "@/components/ui/card"
import { BellIcon, LoaderCircleIcon, PlusIcon, SaveIcon, Trash2Icon } from "lucide-react"
import { ChangeEventHandler, useEffect, useState } from "react"
import { toast } from "@/components/ui/use-toast"
import { InputTags } from "@/components/ui/input-tags"
import { UserSettings } from "@/types"
import { saveSettings } from "./layout"
import * as v from "valibot"
import { isAdmin } from "@/lib/utils"
import { Trans, t } from "@lingui/macro"
interface ShoutrrrUrlCardProps {
url: string
@@ -36,10 +37,10 @@ const SettingsNotificationsPage = ({ userSettings }: { userSettings: UserSetting
}, [userSettings])
function addWebhook() {
setWebhooks([...webhooks, ''])
setWebhooks([...webhooks, ""])
// focus on the new input
queueMicrotask(() => {
const inputs = document.querySelectorAll('#webhooks input') as NodeListOf<HTMLInputElement>
const inputs = document.querySelectorAll("#webhooks input") as NodeListOf<HTMLInputElement>
inputs[inputs.length - 1]?.focus()
})
}
@@ -58,9 +59,9 @@ const SettingsNotificationsPage = ({ userSettings }: { userSettings: UserSetting
await saveSettings(parsedData)
} catch (e: any) {
toast({
title: 'Failed to save settings',
title: t`Failed to save settings`,
description: e.message,
variant: 'destructive',
variant: "destructive",
})
}
setIsLoading(false)
@@ -69,59 +70,67 @@ const SettingsNotificationsPage = ({ userSettings }: { userSettings: UserSetting
return (
<div>
<div>
<h3 className="text-xl font-medium mb-2">Notifications</h3>
<h3 className="text-xl font-medium mb-2">
<Trans>Notifications</Trans>
</h3>
<p className="text-sm text-muted-foreground leading-relaxed">
Configure how you receive alert notifications.
<Trans>Configure how you receive alert notifications.</Trans>
</p>
<p className="text-sm text-muted-foreground mt-1.5 leading-relaxed">
Looking instead for where to create alerts? Click the bell{' '}
<BellIcon className="inline h-4 w-4" /> icons in the systems table.
<Trans>
Looking instead for where to create alerts? Click the bell <BellIcon className="inline h-4 w-4" /> icons in
the systems table.
</Trans>
</p>
</div>
<Separator className="my-4" />
<div className="space-y-5">
<div className="space-y-2">
<div className="mb-4">
<h3 className="mb-1 text-lg font-medium">Email notifications</h3>
<h3 className="mb-1 text-lg font-medium">
<Trans>Email notifications</Trans>
</h3>
{isAdmin() && (
<p className="text-sm text-muted-foreground leading-relaxed">
Please{' '}
<a href="/_/#/settings/mail" className="link" target="_blank">
configure an SMTP server
</a>{' '}
to ensure alerts are delivered.{' '}
<Trans>
Please{" "}
<a href="/_/#/settings/mail" className="link" target="_blank">
configure an SMTP server
</a>{" "}
to ensure alerts are delivered.
</Trans>
</p>
)}
</div>
<Label className="block" htmlFor="email">
To email(s)
<Trans>To email(s)</Trans>
</Label>
<InputTags
value={emails}
onChange={setEmails}
placeholder="Enter email address..."
placeholder={t`Enter email address...`}
className="w-full"
type="email"
id="email"
/>
<p className="text-[0.8rem] text-muted-foreground">
Save address using enter key or comma. Leave blank to disable email notifications.
<Trans>Save address using enter key or comma. Leave blank to disable email notifications.</Trans>
</p>
</div>
<Separator />
<div className="space-y-3">
<div>
<h3 className="mb-1 text-lg font-medium">Webhook / Push notifications</h3>
<h3 className="mb-1 text-lg font-medium">
<Trans>Webhook / Push notifications</Trans>
</h3>
<p className="text-sm text-muted-foreground leading-relaxed">
Beszel uses{' '}
<a
href="https://containrrr.dev/shoutrrr/services/overview/"
target="_blank"
className="link"
>
Shoutrrr
</a>{' '}
to integrate with popular notification services.
<Trans>
Beszel uses{" "}
<a href="https://containrrr.dev/shoutrrr/services/overview/" target="_blank" className="link">
Shoutrrr
</a>{" "}
to integrate with popular notification services.
</Trans>
</p>
</div>
{webhooks.length > 0 && (
@@ -130,9 +139,7 @@ const SettingsNotificationsPage = ({ userSettings }: { userSettings: UserSetting
<ShoutrrrUrlCard
key={index}
url={webhook}
onUrlChange={(e: React.ChangeEvent<HTMLInputElement>) =>
updateWebhook(index, e.target.value)
}
onUrlChange={(e: React.ChangeEvent<HTMLInputElement>) => updateWebhook(index, e.target.value)}
onRemove={() => removeWebhook(index)}
/>
))}
@@ -145,8 +152,8 @@ const SettingsNotificationsPage = ({ userSettings }: { userSettings: UserSetting
className="mt-2 flex items-center gap-1"
onClick={addWebhook}
>
<PlusIcon className="h-4 w-4 -ml-0.5" />
Add URL
<PlusIcon className="h-4 w-4 -ms-0.5" />
<Trans>Add URL</Trans>
</Button>
</div>
<Separator />
@@ -156,12 +163,8 @@ const SettingsNotificationsPage = ({ userSettings }: { userSettings: UserSetting
onClick={updateSettings}
disabled={isLoading}
>
{isLoading ? (
<LoaderCircleIcon className="h-4 w-4 animate-spin" />
) : (
<SaveIcon className="h-4 w-4" />
)}
Save settings
{isLoading ? <LoaderCircleIcon className="h-4 w-4 animate-spin" /> : <SaveIcon className="h-4 w-4" />}
<Trans>Save Settings</Trans>
</Button>
</div>
</div>
@@ -173,17 +176,17 @@ const ShoutrrrUrlCard = ({ url, onUrlChange, onRemove }: ShoutrrrUrlCardProps) =
const sendTestNotification = async () => {
setIsLoading(true)
const res = await pb.send('/api/beszel/send-test-notification', { url })
if ('err' in res && !res.err) {
const res = await pb.send("/api/beszel/send-test-notification", { url })
if ("err" in res && !res.err) {
toast({
title: 'Test notification sent',
description: 'Check your notification service',
title: t`Test notification sent`,
description: t`Check your notification service`,
})
} else {
toast({
title: 'Error',
description: res.err ?? 'Failed to send test notification',
variant: 'destructive',
title: t`Error`,
description: res.err ?? t`Failed to send test notification`,
variant: "destructive",
})
}
setIsLoading(false)
@@ -200,29 +203,18 @@ const ShoutrrrUrlCard = ({ url, onUrlChange, onRemove }: ShoutrrrUrlCardProps) =
value={url}
onChange={onUrlChange}
/>
<Button
type="button"
variant="outline"
className="w-20 md:w-28"
disabled={isLoading || url === ''}
onClick={sendTestNotification}
>
<Button type="button" variant="outline" disabled={isLoading || url === ""} onClick={sendTestNotification}>
{isLoading ? (
<LoaderCircleIcon className="h-4 w-4 animate-spin" />
) : (
<span>
Test <span className="hidden md:inline">URL</span>
<Trans>
Test <span className="hidden sm:inline">URL</span>
</Trans>
</span>
)}
</Button>
<Button
type="button"
variant="outline"
size="icon"
className="shrink-0"
aria-label="Delete"
onClick={onRemove}
>
<Button type="button" variant="outline" size="icon" className="shrink-0" aria-label="Delete" onClick={onRemove}>
<Trash2Icon className="h-4 w-4" />
</Button>
</div>

View File

@@ -1,22 +1,17 @@
import React from 'react'
import { cn } from '@/lib/utils'
import { buttonVariants } from '../../ui/button'
import { $router, Link, navigate } from '../../router'
import { useStore } from '@nanostores/react'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
import { Separator } from '@/components/ui/separator'
import React from "react"
import { cn, isAdmin } from "@/lib/utils"
import { buttonVariants } from "../../ui/button"
import { $router, Link, navigate } from "../../router"
import { useStore } from "@nanostores/react"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Separator } from "@/components/ui/separator"
interface SidebarNavProps extends React.HTMLAttributes<HTMLElement> {
items: {
href: string
title: string
icon?: React.FC<React.SVGProps<SVGSVGElement>>
admin?: boolean
}[]
}
@@ -29,33 +24,36 @@ export function SidebarNav({ className, items, ...props }: SidebarNavProps) {
<div className="md:hidden">
<Select onValueChange={(value: string) => navigate(value)} value={page?.path}>
<SelectTrigger className="w-full my-3.5">
<SelectValue placeholder="Select a page" />
<SelectValue placeholder="Select page" />
</SelectTrigger>
<SelectContent>
{items.map((item) => (
<SelectItem key={item.href} value={item.href}>
<span className="flex items-center gap-2">
{item.icon && <item.icon className="h-4 w-4" />}
{item.title}
</span>
</SelectItem>
))}
{items.map((item) => {
if (item.admin && !isAdmin()) return null
return (
<SelectItem key={item.href} value={item.href}>
<span className="flex items-center gap-2">
{item.icon && <item.icon className="h-4 w-4" />}
{item.title}
</span>
</SelectItem>
)
})}
</SelectContent>
</Select>
<Separator />
</div>
{/* Desktop View */}
<nav className={cn('hidden md:grid gap-1', className)} {...props}>
<nav className={cn("hidden md:grid gap-1", className)} {...props}>
{items.map((item) => (
<Link
key={item.href}
href={item.href}
className={cn(
buttonVariants({ variant: 'ghost' }),
'flex items-center gap-3',
page?.path === item.href ? 'bg-muted hover:bg-muted' : 'hover:bg-muted/50',
'justify-start'
buttonVariants({ variant: "ghost" }),
"flex items-center gap-3",
page?.path === item.href ? "bg-muted hover:bg-muted" : "hover:bg-muted/50",
"justify-start"
)}
>
{item.icon && <item.icon className="h-4 w-4" />}

View File

@@ -1,39 +1,35 @@
import { $systems, pb, $chartTime, $containerFilter, $userSettings } from '@/lib/stores'
import {
ChartData,
ChartTimes,
ContainerStatsRecord,
SystemRecord,
SystemStatsRecord,
} from '@/types'
import React, { lazy, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Card, CardHeader, CardTitle, CardDescription } from '../ui/card'
import { useStore } from '@nanostores/react'
import Spinner from '../spinner'
import { ClockArrowUp, CpuIcon, GlobeIcon, LayoutGridIcon, MonitorIcon, XIcon } from 'lucide-react'
import ChartTimeSelect from '../charts/chart-time-select'
import { chartTimeData, cn, getPbTimestamp, useLocalStorage } from '@/lib/utils'
import { Separator } from '../ui/separator'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../ui/tooltip'
import { Button } from '../ui/button'
import { Input } from '../ui/input'
import { ChartAverage, ChartMax, Rows, TuxIcon } from '../ui/icons'
import { useIntersectionObserver } from '@/lib/use-intersection-observer'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select'
import { timeTicks } from 'd3-time'
import { $systems, pb, $chartTime, $containerFilter, $userSettings, $direction } from "@/lib/stores"
import { ChartData, ChartTimes, ContainerStatsRecord, SystemRecord, SystemStatsRecord } from "@/types"
import React, { lazy, useCallback, useEffect, useMemo, useRef, useState } from "react"
import { Card, CardHeader, CardTitle, CardDescription } from "../ui/card"
import { useStore } from "@nanostores/react"
import Spinner from "../spinner"
import { ClockArrowUp, CpuIcon, GlobeIcon, LayoutGridIcon, MonitorIcon, XIcon } from "lucide-react"
import ChartTimeSelect from "../charts/chart-time-select"
import { chartTimeData, cn, getPbTimestamp, useLocalStorage } from "@/lib/utils"
import { Separator } from "../ui/separator"
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../ui/tooltip"
import { Button } from "../ui/button"
import { Input } from "../ui/input"
import { ChartAverage, ChartMax, Rows, TuxIcon } from "../ui/icons"
import { useIntersectionObserver } from "@/lib/use-intersection-observer"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select"
import { timeTicks } from "d3-time"
import { Plural, Trans, t } from "@lingui/macro"
import { useLingui } from "@lingui/react"
const AreaChartDefault = lazy(() => import('../charts/area-chart'))
const ContainerChart = lazy(() => import('../charts/container-chart'))
const MemChart = lazy(() => import('../charts/mem-chart'))
const DiskChart = lazy(() => import('../charts/disk-chart'))
const SwapChart = lazy(() => import('../charts/swap-chart'))
const TemperatureChart = lazy(() => import('../charts/temperature-chart'))
const AreaChartDefault = lazy(() => import("../charts/area-chart"))
const ContainerChart = lazy(() => import("../charts/container-chart"))
const MemChart = lazy(() => import("../charts/mem-chart"))
const DiskChart = lazy(() => import("../charts/disk-chart"))
const SwapChart = lazy(() => import("../charts/swap-chart"))
const TemperatureChart = lazy(() => import("../charts/temperature-chart"))
const cache = new Map<string, any>()
// create ticks and domain for charts
function getTimeData(chartTime: ChartTimes, lastCreated: number) {
const cached = cache.get('td')
const cached = cache.get("td")
if (cached && cached.chartTime === chartTime) {
if (!lastCreated || cached.time >= lastCreated) {
return cached.data
@@ -42,14 +38,12 @@ function getTimeData(chartTime: ChartTimes, lastCreated: number) {
const now = new Date()
const startTime = chartTimeData[chartTime].getOffset(now)
const ticks = timeTicks(startTime, now, chartTimeData[chartTime].ticks ?? 12).map((date) =>
date.getTime()
)
const ticks = timeTicks(startTime, now, chartTimeData[chartTime].ticks ?? 12).map((date) => date.getTime())
const data = {
ticks,
domain: [chartTimeData[chartTime].getOffset(now).getTime(), now.getTime()],
}
cache.set('td', { time: now.getTime(), data, chartTime })
cache.set("td", { time: now.getTime(), data, chartTime })
return data
}
@@ -78,38 +72,37 @@ function addEmptyValues<T extends SystemStatsRecord | ContainerStatsRecord>(
return modifiedRecords
}
async function getStats<T>(
collection: string,
system: SystemRecord,
chartTime: ChartTimes
): Promise<T[]> {
async function getStats<T>(collection: string, system: SystemRecord, chartTime: ChartTimes): Promise<T[]> {
const lastCached = cache.get(`${system.id}_${chartTime}_${collection}`)?.at(-1)?.created as number
return await pb.collection<T>(collection).getFullList({
filter: pb.filter('system={:id} && created > {:created} && type={:type}', {
filter: pb.filter("system={:id} && created > {:created} && type={:type}", {
id: system.id,
created: getPbTimestamp(chartTime, lastCached ? new Date(lastCached + 1000) : undefined),
type: chartTimeData[chartTime].type,
}),
fields: 'created,stats',
sort: 'created',
fields: "created,stats",
sort: "created",
})
}
export default function SystemDetail({ name }: { name: string }) {
const direction = useStore($direction)
const { _ } = useLingui()
const systems = useStore($systems)
const chartTime = useStore($chartTime)
/** Max CPU toggle value */
const cpuMaxStore = useState(false)
const bandwidthMaxStore = useState(false)
const diskIoMaxStore = useState(false)
const [grid, setGrid] = useLocalStorage('grid', true)
const [grid, setGrid] = useLocalStorage("grid", true)
const [system, setSystem] = useState({} as SystemRecord)
const [systemStats, setSystemStats] = useState([] as SystemStatsRecord[])
const [containerData, setContainerData] = useState([] as ChartData['containerData'])
const [containerData, setContainerData] = useState([] as ChartData["containerData"])
const netCardRef = useRef<HTMLDivElement>(null)
const [containerFilterBar, setContainerFilterBar] = useState(null as null | JSX.Element)
const [bottomSpacing, setBottomSpacing] = useState(0)
const isLongerChart = chartTime !== '1h'
const [chartLoading, setChartLoading] = useState(true)
const isLongerChart = chartTime !== "1h"
useEffect(() => {
document.title = `${name} / Beszel`
@@ -119,7 +112,7 @@ export default function SystemDetail({ name }: { name: string }) {
setSystemStats([])
setContainerData([])
setContainerFilterBar(null)
$containerFilter.set('')
$containerFilter.set("")
cpuMaxStore[1](false)
bandwidthMaxStore[1](false)
diskIoMaxStore[1](false)
@@ -149,11 +142,11 @@ export default function SystemDetail({ name }: { name: string }) {
if (!system.id) {
return
}
pb.collection<SystemRecord>('systems').subscribe(system.id, (e) => {
pb.collection<SystemRecord>("systems").subscribe(system.id, (e) => {
setSystem(e.record)
})
return () => {
pb.collection('systems').unsubscribe(system.id)
pb.collection("systems").unsubscribe(system.id)
}
}, [system.id])
@@ -166,27 +159,31 @@ export default function SystemDetail({ name }: { name: string }) {
systemStats,
containerData,
chartTime,
orientation: direction === "rtl" ? "right" : "left",
...getTimeData(chartTime, lastCreated),
}
}, [systemStats, containerData])
}, [systemStats, containerData, direction])
// get stats
useEffect(() => {
if (!system.id || !chartTime) {
return
}
// loading: true
setChartLoading(true)
Promise.allSettled([
getStats<SystemStatsRecord>('system_stats', system, chartTime),
getStats<ContainerStatsRecord>('container_stats', system, chartTime),
getStats<SystemStatsRecord>("system_stats", system, chartTime),
getStats<ContainerStatsRecord>("container_stats", system, chartTime),
]).then(([systemStats, containerStats]) => {
// loading: false
setChartLoading(false)
const { expectedInterval } = chartTimeData[chartTime]
// make new system stats
const ss_cache_key = `${system.id}_${chartTime}_system_stats`
let systemData = (cache.get(ss_cache_key) || []) as SystemStatsRecord[]
if (systemStats.status === 'fulfilled' && systemStats.value.length) {
systemData = systemData.concat(
addEmptyValues(systemData, systemStats.value, expectedInterval)
)
if (systemStats.status === "fulfilled" && systemStats.value.length) {
systemData = systemData.concat(addEmptyValues(systemData, systemStats.value, expectedInterval))
if (systemData.length > 120) {
systemData = systemData.slice(-100)
}
@@ -196,10 +193,8 @@ export default function SystemDetail({ name }: { name: string }) {
// make new container stats
const cs_cache_key = `${system.id}_${chartTime}_container_stats`
let containerData = (cache.get(cs_cache_key) || []) as ContainerStatsRecord[]
if (containerStats.status === 'fulfilled' && containerStats.value.length) {
containerData = containerData.concat(
addEmptyValues(containerData, containerStats.value, expectedInterval)
)
if (containerStats.status === "fulfilled" && containerStats.value.length) {
containerData = containerData.concat(addEmptyValues(containerData, containerStats.value, expectedInterval))
if (containerData.length > 120) {
containerData = containerData.slice(-100)
}
@@ -216,7 +211,7 @@ export default function SystemDetail({ name }: { name: string }) {
// make container stats for charts
const makeContainerData = useCallback((containers: ContainerStatsRecord[]) => {
const containerData = [] as ChartData['containerData']
const containerData = [] as ChartData["containerData"]
for (let { created, stats } of containers) {
if (!created) {
// @ts-ignore add null value for gaps
@@ -225,7 +220,7 @@ export default function SystemDetail({ name }: { name: string }) {
}
created = new Date(created).getTime()
// @ts-ignore not dealing with this rn
let containerStats: ChartData['containerData'][0] = { created }
let containerStats: ChartData["containerData"][0] = { created }
for (let container of stats) {
containerStats[container.n] = container
}
@@ -239,26 +234,26 @@ export default function SystemDetail({ name }: { name: string }) {
if (!system.info) {
return []
}
let uptime: number | string = system.info.u
let uptime: React.ReactNode
if (system.info.u < 172800) {
const hours = Math.trunc(uptime / 3600)
uptime = `${hours} hour${hours == 1 ? '' : 's'}`
const hours = Math.trunc(system.info.u / 3600)
uptime = <Plural value={hours} one="# hour" other="# hours" />
} else {
uptime = `${Math.trunc(system.info?.u / 86400)} days`
uptime = <Plural value={Math.trunc(system.info?.u / 86400)} one="# day" other="# days" />
}
return [
{ value: system.host, Icon: GlobeIcon },
{
value: system.info.h,
Icon: MonitorIcon,
label: 'Hostname',
label: "Hostname",
// hide if hostname is same as host or name
hide: system.info.h === system.host || system.info.h === system.name,
},
{ value: uptime, Icon: ClockArrowUp, label: 'Uptime' },
{ value: system.info.k, Icon: TuxIcon, label: 'Kernel' },
{ value: uptime, Icon: ClockArrowUp, label: t`Uptime` },
{ value: system.info.k, Icon: TuxIcon, label: t({ comment: "Linux kernel", message: "Kernel" }) },
{
value: `${system.info.m} (${system.info.c}c${system.info.t ? `/${system.info.t}t` : ''})`,
value: `${system.info.m} (${system.info.c}c${system.info.t ? `/${system.info.t}t` : ""})`,
Icon: CpuIcon,
hide: !system.info.m,
},
@@ -277,7 +272,7 @@ export default function SystemDetail({ name }: { name: string }) {
return
}
const tooltipHeight = (Object.keys(containerData[0]).length - 11) * 17.8 - 40
const wrapperEl = document.getElementById('chartwrap') as HTMLDivElement
const wrapperEl = document.getElementById("chartwrap") as HTMLDivElement
const wrapperRect = wrapperEl.getBoundingClientRect()
const chartRect = netCardRef.current.getBoundingClientRect()
const distanceToBottom = wrapperRect.bottom - chartRect.bottom
@@ -288,29 +283,32 @@ export default function SystemDetail({ name }: { name: string }) {
return null
}
// if no data, show empty message
const dataEmpty = !chartLoading && chartData.systemStats.length === 0
return (
<>
<div id="chartwrap" className="grid gap-4 mb-10 overflow-x-clip">
{/* system info */}
<Card>
<div className="grid lg:flex gap-4 px-4 sm:px-6 pt-3 sm:pt-4 pb-5">
<div className="grid xl:flex gap-4 px-4 sm:px-6 pt-3 sm:pt-4 pb-5">
<div>
<h1 className="text-[1.6rem] font-semibold mb-1.5">{system.name}</h1>
<div className="flex flex-wrap items-center gap-3 gap-y-2 text-sm opacity-90">
<div className="capitalize flex gap-2 items-center">
<span className={cn('relative flex h-3 w-3')}>
{system.status === 'up' && (
<span className={cn("relative flex h-3 w-3")}>
{system.status === "up" && (
<span
className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"
style={{ animationDuration: '1.5s' }}
style={{ animationDuration: "1.5s" }}
></span>
)}
<span
className={cn('relative inline-flex rounded-full h-3 w-3', {
'bg-green-500': system.status === 'up',
'bg-red-500': system.status === 'down',
'bg-primary/40': system.status === 'paused',
'bg-yellow-500': system.status === 'pending',
className={cn("relative inline-flex rounded-full h-3 w-3", {
"bg-green-500": system.status === "up",
"bg-red-500": system.status === "down",
"bg-primary/40": system.status === "paused",
"bg-yellow-500": system.status === "pending",
})}
></span>
</span>
@@ -343,16 +341,16 @@ export default function SystemDetail({ name }: { name: string }) {
})}
</div>
</div>
<div className="lg:ml-auto flex items-center gap-2 max-sm:-mb-1">
<ChartTimeSelect className="w-full lg:w-40" />
<div className="xl:ms-auto flex items-center gap-2 max-sm:-mb-1">
<ChartTimeSelect className="w-full xl:w-40" />
<TooltipProvider delayDuration={100}>
<Tooltip>
<TooltipTrigger asChild>
<Button
aria-label="Toggle grid"
aria-label={t`Toggle grid`}
variant="outline"
size="icon"
className="hidden lg:flex p-0 text-primary"
className="hidden xl:flex p-0 text-primary"
onClick={() => setGrid(!grid)}
>
{grid ? (
@@ -362,7 +360,7 @@ export default function SystemDetail({ name }: { name: string }) {
)}
</Button>
</TooltipTrigger>
<TooltipContent>Toggle grid</TooltipContent>
<TooltipContent>{t`Toggle grid`}</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
@@ -370,28 +368,23 @@ export default function SystemDetail({ name }: { name: string }) {
</Card>
{/* main charts */}
<div className="grid lg:grid-cols-2 gap-4">
<div className="grid xl:grid-cols-2 gap-4">
<ChartCard
empty={dataEmpty}
grid={grid}
title="Total CPU Usage"
description={`${
cpuMaxStore[0] && isLongerChart ? 'Max 1 min ' : 'Average'
} system-wide CPU utilization`}
title={_(t`CPU Usage`)}
description={t`Average system-wide CPU utilization`}
cornerEl={isLongerChart ? <SelectAvgMax store={cpuMaxStore} /> : null}
>
<AreaChartDefault
chartData={chartData}
chartName="CPU Usage"
maxToggled={cpuMaxStore[0]}
unit="%"
/>
<AreaChartDefault chartData={chartData} chartName="CPU Usage" maxToggled={cpuMaxStore[0]} unit="%" />
</ChartCard>
{containerFilterBar && (
<ChartCard
empty={dataEmpty}
grid={grid}
title="Docker CPU Usage"
description="Average CPU utilization of containers"
title={t`Docker CPU Usage`}
description={t`Average CPU utilization of containers`}
cornerEl={containerFilterBar}
>
<ContainerChart chartData={chartData} dataKey="c" chartName="cpu" />
@@ -399,25 +392,27 @@ export default function SystemDetail({ name }: { name: string }) {
)}
<ChartCard
empty={dataEmpty}
grid={grid}
title="Total Memory Usage"
description="Precise utilization at the recorded time"
title={t`Memory Usage`}
description={t`Precise utilization at the recorded time`}
>
<MemChart chartData={chartData} />
</ChartCard>
{containerFilterBar && (
<ChartCard
empty={dataEmpty}
grid={grid}
title="Docker Memory Usage"
description="Memory usage of docker containers"
title={t`Docker Memory Usage`}
description={t`Memory usage of docker containers`}
cornerEl={containerFilterBar}
>
<ContainerChart chartData={chartData} chartName="mem" dataKey="m" unit=" MB" />
</ChartCard>
)}
<ChartCard grid={grid} title="Disk Space" description="Usage of root partition">
<ChartCard empty={dataEmpty} grid={grid} title={t`Disk Usage`} description={t`Usage of root partition`}>
<DiskChart
chartData={chartData}
dataKey="stats.du"
@@ -426,41 +421,36 @@ export default function SystemDetail({ name }: { name: string }) {
</ChartCard>
<ChartCard
empty={dataEmpty}
grid={grid}
title="Disk I/O"
description="Throughput of root filesystem"
title={t`Disk I/O`}
description={t`Throughput of root filesystem`}
cornerEl={isLongerChart ? <SelectAvgMax store={diskIoMaxStore} /> : null}
>
<AreaChartDefault
chartData={chartData}
maxToggled={diskIoMaxStore[0]}
chartName="dio"
/>
<AreaChartDefault chartData={chartData} maxToggled={diskIoMaxStore[0]} chartName="dio" />
</ChartCard>
<ChartCard
empty={dataEmpty}
grid={grid}
title="Bandwidth"
title={t`Bandwidth`}
cornerEl={isLongerChart ? <SelectAvgMax store={bandwidthMaxStore} /> : null}
description="Network traffic of public interfaces"
description={t`Network traffic of public interfaces`}
>
<AreaChartDefault
chartData={chartData}
maxToggled={bandwidthMaxStore[0]}
chartName="bw"
/>
<AreaChartDefault chartData={chartData} maxToggled={bandwidthMaxStore[0]} chartName="bw" />
</ChartCard>
{containerFilterBar && containerData.length > 0 && (
<div
ref={netCardRef}
className={cn({
'col-span-full': !grid,
"col-span-full": !grid,
})}
>
<ChartCard
title="Docker Network I/O"
description="Includes traffic between internal services"
empty={dataEmpty}
title={t`Docker Network I/O`}
description={t`Network traffic of docker containers`}
cornerEl={containerFilterBar}
>
{/* @ts-ignore */}
@@ -470,13 +460,23 @@ export default function SystemDetail({ name }: { name: string }) {
)}
{(systemStats.at(-1)?.stats.su ?? 0) > 0 && (
<ChartCard grid={grid} title="Swap Usage" description="Swap space used by the system">
<ChartCard
empty={dataEmpty}
grid={grid}
title={t`Swap Usage`}
description={t`Swap space used by the system`}
>
<SwapChart chartData={chartData} />
</ChartCard>
)}
{systemStats.at(-1)?.stats.t && (
<ChartCard grid={grid} title="Temperature" description="Temperatures of system sensors">
<ChartCard
empty={dataEmpty}
grid={grid}
title={t`Temperature`}
description={t`Temperatures of system sensors`}
>
<TemperatureChart chartData={chartData} />
</ChartCard>
)}
@@ -484,14 +484,15 @@ export default function SystemDetail({ name }: { name: string }) {
{/* extra filesystem charts */}
{Object.keys(systemStats.at(-1)?.stats.efs ?? {}).length > 0 && (
<div className="grid lg:grid-cols-2 gap-4">
<div className="grid xl:grid-cols-2 gap-4">
{Object.keys(systemStats.at(-1)?.stats.efs ?? {}).map((extraFsName) => {
return (
<div key={extraFsName} className="contents">
<ChartCard
empty={dataEmpty}
grid={grid}
title={`${extraFsName} Usage`}
description={`Disk usage of ${extraFsName}`}
title={`${extraFsName} ${t`Usage`}`}
description={t`Disk usage of ${extraFsName}`}
>
<DiskChart
chartData={chartData}
@@ -500,9 +501,10 @@ export default function SystemDetail({ name }: { name: string }) {
/>
</ChartCard>
<ChartCard
empty={dataEmpty}
grid={grid}
title={`${extraFsName} I/O`}
description={`Throughput of ${extraFsName}`}
description={t`Throughput of ${extraFsName}`}
cornerEl={isLongerChart ? <SelectAvgMax store={diskIoMaxStore} /> : null}
>
<AreaChartDefault
@@ -526,6 +528,7 @@ export default function SystemDetail({ name }: { name: string }) {
function ContainerFilterBar() {
const containerFilter = useStore($containerFilter)
const { _ } = useLingui()
const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
$containerFilter.set(e.target.value)
@@ -533,12 +536,7 @@ function ContainerFilterBar() {
return (
<>
<Input
placeholder="Filter..."
className="pl-4 pr-8"
value={containerFilter}
onChange={handleChange}
/>
<Input placeholder={_(t`Filter...`)} className="ps-4 pe-8" value={containerFilter} onChange={handleChange} />
{containerFilter && (
<Button
type="button"
@@ -546,7 +544,7 @@ function ContainerFilterBar() {
size="icon"
aria-label="Clear"
className="absolute right-1 top-1/2 -translate-y-1/2 h-7 w-7 text-gray-500 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-100"
onClick={() => $containerFilter.set('')}
onClick={() => $containerFilter.set("")}
>
<XIcon className="h-4 w-4" />
</Button>
@@ -555,26 +553,22 @@ function ContainerFilterBar() {
)
}
function SelectAvgMax({
store,
}: {
store: [boolean, React.Dispatch<React.SetStateAction<boolean>>]
}) {
function SelectAvgMax({ store }: { store: [boolean, React.Dispatch<React.SetStateAction<boolean>>] }) {
const [max, setMax] = store
const Icon = max ? ChartMax : ChartAverage
return (
<Select value={max ? 'max' : 'avg'} onValueChange={(e) => setMax(e === 'max')}>
<SelectTrigger className="relative pl-10 pr-5">
<Icon className="h-4 w-4 absolute left-4 top-1/2 -translate-y-1/2 opacity-85" />
<Select value={max ? "max" : "avg"} onValueChange={(e) => setMax(e === "max")}>
<SelectTrigger className="relative ps-10 pe-5">
<Icon className="h-4 w-4 absolute start-4 top-1/2 -translate-y-1/2 opacity-85" />
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem key="avg" value="avg">
Average
<Trans>Average</Trans>
</SelectItem>
<SelectItem key="max" value="max">
Max 1 min
<Trans comment="Chart select field. Please try to keep this short.">Max 1 min</Trans>
</SelectItem>
</SelectContent>
</Select>
@@ -586,32 +580,27 @@ function ChartCard({
description,
children,
grid,
empty,
cornerEl,
}: {
title: string
description: string
children: React.ReactNode
grid?: boolean
empty?: boolean
cornerEl?: JSX.Element | null
}) {
const { isIntersecting, ref } = useIntersectionObserver()
return (
<Card
className={cn('pb-2 sm:pb-4 odd:last-of-type:col-span-full', { 'col-span-full': !grid })}
ref={ref}
>
<Card className={cn("pb-2 sm:pb-4 odd:last-of-type:col-span-full", { "col-span-full": !grid })} ref={ref}>
<CardHeader className="pb-5 pt-4 relative space-y-1 max-sm:py-3 max-sm:px-4">
<CardTitle className="text-xl sm:text-2xl">{title}</CardTitle>
<CardDescription>{description}</CardDescription>
{cornerEl && (
<div className="relative py-1 block sm:w-44 sm:absolute sm:top-2.5 sm:right-3.5">
{cornerEl}
</div>
)}
{cornerEl && <div className="relative py-1 block sm:w-44 sm:absolute sm:top-2.5 sm:end-3.5">{cornerEl}</div>}
</CardHeader>
<div className="pl-0 w-[calc(100%-1.6em)] h-52 relative">
{<Spinner />}
<div className="ps-0 w-[calc(100%-1.6em)] h-52 relative">
{<Spinner msg={empty ? t`Waiting for enough records to display` : undefined} />}
{isIntersecting && children}
</div>
</Card>

View File

@@ -1,9 +1,13 @@
import { LoaderCircleIcon } from 'lucide-react'
import { LoaderCircleIcon } from "lucide-react"
export default function () {
export default function ({ msg }: { msg?: string }) {
return (
<div className="grid place-content-center h-full absolute inset-0">
<LoaderCircleIcon className="animate-spin h-10 w-10 opacity-60" />
<div className="flex flex-col items-center justify-center h-full absolute inset-0">
{msg ? (
<p className={"opacity-60 mb-2 text-center px-4"}>{msg}</p>
) : (
<LoaderCircleIcon className="animate-spin h-10 w-10 opacity-60" />
)}
</div>
)
}

View File

@@ -6,29 +6,24 @@ import {
SortingState,
getSortedRowModel,
flexRender,
VisibilityState,
getCoreRowModel,
useReactTable,
Column,
} from '@tanstack/react-table'
} from "@tanstack/react-table"
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { Button, buttonVariants } from '@/components/ui/button'
import { Button, buttonVariants } from "@/components/ui/button"
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
} from "@/components/ui/dropdown-menu"
import {
AlertDialog,
@@ -40,9 +35,9 @@ import {
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from '@/components/ui/alert-dialog'
} from "@/components/ui/alert-dialog"
import { SystemRecord } from '@/types'
import { SystemRecord } from "@/types"
import {
MoreHorizontalIcon,
ArrowUpDownIcon,
@@ -55,14 +50,19 @@ import {
HardDriveIcon,
ServerIcon,
CpuIcon,
} from 'lucide-react'
import { useEffect, useMemo, useState } from 'react'
import { $hubVersion, $systems, pb } from '@/lib/stores'
import { useStore } from '@nanostores/react'
import { cn, copyToClipboard, decimalString, isReadOnlyUser } from '@/lib/utils'
import AlertsButton from '../alerts/alert-button'
import { navigate } from '../router'
import { EthernetIcon } from '../ui/icons'
ChevronDownIcon,
} from "lucide-react"
import { useEffect, useMemo, useState } from "react"
import { $hubVersion, $systems, pb } from "@/lib/stores"
import { useStore } from "@nanostores/react"
import { cn, copyToClipboard, decimalString, isReadOnlyUser, useLocalStorage } from "@/lib/utils"
import AlertsButton from "../alerts/alert-button"
import { navigate } from "../router"
import { EthernetIcon } from "../ui/icons"
import { Trans, t } from "@lingui/macro"
import { useLingui } from "@lingui/react"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card"
import { Input } from "../ui/input"
function CellFormatter(info: CellContext<SystemRecord, unknown>) {
const val = info.getValue() as number
@@ -72,8 +72,8 @@ function CellFormatter(info: CellContext<SystemRecord, unknown>) {
<span className="grow min-w-10 block bg-muted h-[1em] relative rounded-sm overflow-hidden">
<span
className={cn(
'absolute inset-0 w-full h-full origin-left',
(val < 65 && 'bg-green-500') || (val < 90 && 'bg-yellow-500') || 'bg-red-600'
"absolute inset-0 w-full h-full origin-left",
(val < 65 && "bg-green-500") || (val < 90 && "bg-yellow-500") || "bg-red-600"
)}
style={{ transform: `scalex(${val}%)` }}
></span>
@@ -82,34 +82,32 @@ function CellFormatter(info: CellContext<SystemRecord, unknown>) {
)
}
function sortableHeader(
column: Column<SystemRecord, unknown>,
name: string,
Icon: any,
hideSortIcon = false
) {
function sortableHeader(column: Column<SystemRecord, unknown>, Icon: any, hideSortIcon = false) {
return (
<Button
variant="ghost"
className="h-9 px-3"
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
className="h-9 px-3 flex"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
<Icon className="mr-2 h-4 w-4" />
{name}
{!hideSortIcon && <ArrowUpDownIcon className="ml-2 h-4 w-4" />}
<Icon className="me-2 h-4 w-4" />
{column.id}
{!hideSortIcon && <ArrowUpDownIcon className="ms-2 h-4 w-4" />}
</Button>
)
}
export default function SystemsTable({ filter }: { filter?: string }) {
export default function SystemsTable() {
const data = useStore($systems)
const hubVersion = useStore($hubVersion)
const [filter, setFilter] = useState<string>()
const [sorting, setSorting] = useState<SortingState>([])
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([])
const [columnVisibility, setColumnVisibility] = useLocalStorage<VisibilityState>("cols", {})
const { i18n } = useLingui()
useEffect(() => {
if (filter !== undefined) {
table.getColumn('name')?.setFilterValue(filter)
table.getColumn(t`System`)?.setFilterValue(filter)
}
}, [filter])
@@ -119,23 +117,25 @@ export default function SystemsTable({ filter }: { filter?: string }) {
// size: 200,
size: 200,
minSize: 0,
accessorKey: 'name',
accessorKey: "name",
id: t`System`,
enableHiding: false,
cell: (info) => {
const { status } = info.row.original
return (
<span className="flex gap-0.5 items-center text-base md:pr-5">
<span className="flex gap-0.5 items-center text-base md:pe-5">
<span
className={cn('w-2 h-2 left-0 rounded-full', {
'bg-green-500': status === 'up',
'bg-red-500': status === 'down',
'bg-primary/40': status === 'paused',
'bg-yellow-500': status === 'pending',
className={cn("w-2 h-2 left-0 rounded-full", {
"bg-green-500": status === "up",
"bg-red-500": status === "down",
"bg-primary/40": status === "paused",
"bg-yellow-500": status === "pending",
})}
style={{ marginBottom: '-1px' }}
style={{ marginBottom: "-1px" }}
></span>
<Button
data-nolink
variant={'ghost'}
variant={"ghost"}
className="text-primary/90 h-7 px-1.5 gap-1.5"
onClick={() => copyToClipboard(info.getValue() as string)}
>
@@ -145,59 +145,58 @@ export default function SystemsTable({ filter }: { filter?: string }) {
</span>
)
},
header: ({ column }) => sortableHeader(column, 'System', ServerIcon),
header: ({ column }) => sortableHeader(column, ServerIcon),
},
{
accessorKey: 'info.cpu',
accessorKey: "info.cpu",
id: t`CPU`,
invertSorting: true,
cell: CellFormatter,
header: ({ column }) => sortableHeader(column, 'CPU', CpuIcon),
header: ({ column }) => sortableHeader(column, CpuIcon),
},
{
accessorKey: 'info.mp',
accessorKey: "info.mp",
id: t`Memory`,
invertSorting: true,
cell: CellFormatter,
header: ({ column }) => sortableHeader(column, 'Memory', MemoryStickIcon),
header: ({ column }) => sortableHeader(column, MemoryStickIcon),
},
{
accessorKey: 'info.dp',
accessorKey: "info.dp",
id: t`Disk`,
invertSorting: true,
cell: CellFormatter,
header: ({ column }) => sortableHeader(column, 'Disk', HardDriveIcon),
header: ({ column }) => sortableHeader(column, HardDriveIcon),
},
{
accessorFn: (originalRow) => originalRow.info.b || 0,
id: 'n',
id: t`Net`,
invertSorting: true,
size: 115,
header: ({ column }) => sortableHeader(column, 'Net', EthernetIcon),
header: ({ column }) => sortableHeader(column, EthernetIcon),
cell: (info) => {
const val = info.getValue() as number
return (
<span className="tabular-nums whitespace-nowrap pl-1">
{decimalString(val, val >= 100 ? 1 : 2)} MB/s
</span>
<span className="tabular-nums whitespace-nowrap ps-1">{decimalString(val, val >= 100 ? 1 : 2)} MB/s</span>
)
},
},
{
accessorKey: 'info.v',
accessorKey: "info.v",
id: t`Agent`,
invertSorting: true,
size: 50,
header: ({ column }) => sortableHeader(column, 'Agent', WifiIcon, true),
header: ({ column }) => sortableHeader(column, WifiIcon, true),
cell: (info) => {
const version = info.getValue() as string
if (!version || !hubVersion) {
return null
}
return (
<span className="flex gap-2 items-center md:pr-5 tabular-nums pl-1">
<span className="flex gap-2 items-center md:pe-5 tabular-nums ps-1">
<span
className={cn(
'w-2 h-2 left-0 rounded-full',
version === hubVersion ? 'bg-green-500' : 'bg-yellow-500'
)}
style={{ marginBottom: '-1px' }}
className={cn("w-2 h-2 left-0 rounded-full", version === hubVersion ? "bg-green-500" : "bg-yellow-500")}
style={{ marginBottom: "-1px" }}
></span>
<span>{info.getValue() as string}</span>
</span>
@@ -205,72 +204,78 @@ export default function SystemsTable({ filter }: { filter?: string }) {
},
},
{
id: 'actions',
id: t({ message: "Actions", comment: "Table column" }),
size: 120,
// minSize: 0,
cell: ({ row }) => {
const { id, name, status, host } = row.original
return (
<div className={'flex justify-end items-center gap-1'}>
<div className={"flex justify-end items-center gap-1"}>
<AlertsButton system={row.original} />
<AlertDialog>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size={'icon'} data-nolink>
<span className="sr-only">Open menu</span>
<Button variant="ghost" size={"icon"} data-nolink>
<span className="sr-only">
<Trans>Open menu</Trans>
</span>
<MoreHorizontalIcon className="w-5" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem
className={cn(isReadOnlyUser() && 'hidden')}
className={cn(isReadOnlyUser() && "hidden")}
onClick={() => {
pb.collection('systems').update(id, {
status: status === 'paused' ? 'pending' : 'paused',
pb.collection("systems").update(id, {
status: status === "paused" ? "pending" : "paused",
})
}}
>
{status === 'paused' ? (
{status === "paused" ? (
<>
<PlayCircleIcon className="mr-2.5 h-4 w-4" />
Resume
<PlayCircleIcon className="me-2.5 h-4 w-4" />
<Trans>Resume</Trans>
</>
) : (
<>
<PauseCircleIcon className="mr-2.5 h-4 w-4" />
Pause
<PauseCircleIcon className="me-2.5 h-4 w-4" />
<Trans>Pause</Trans>
</>
)}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => copyToClipboard(host)}>
<CopyIcon className="mr-2.5 h-4 w-4" />
Copy host
<CopyIcon className="me-2.5 h-4 w-4" />
<Trans>Copy host</Trans>
</DropdownMenuItem>
<DropdownMenuSeparator className={cn(isReadOnlyUser() && 'hidden')} />
<DropdownMenuSeparator className={cn(isReadOnlyUser() && "hidden")} />
<AlertDialogTrigger asChild>
<DropdownMenuItem className={cn(isReadOnlyUser() && 'hidden')}>
<Trash2Icon className="mr-2.5 h-4 w-4" />
Delete
<DropdownMenuItem className={cn(isReadOnlyUser() && "hidden")}>
<Trash2Icon className="me-2.5 h-4 w-4" />
<Trans>Delete</Trans>
</DropdownMenuItem>
</AlertDialogTrigger>
</DropdownMenuContent>
</DropdownMenu>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you sure you want to delete {name}?</AlertDialogTitle>
<AlertDialogTitle>
<Trans>Are you sure you want to delete {name}?</Trans>
</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete all current records
for <code className={'bg-muted rounded-sm px-1'}>{name}</code> from the
database.
<Trans>
This action cannot be undone. This will permanently delete all current records for {name} from
the database.
</Trans>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogCancel>
<Trans>Cancel</Trans>
</AlertDialogCancel>
<AlertDialogAction
className={cn(buttonVariants({ variant: 'destructive' }))}
onClick={() => pb.collection('systems').delete(id)}
className={cn(buttonVariants({ variant: "destructive" }))}
onClick={() => pb.collection("systems").delete(id)}
>
Continue
<Trans>Continue</Trans>
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
@@ -280,7 +285,7 @@ export default function SystemsTable({ filter }: { filter?: string }) {
},
},
] as ColumnDef<SystemRecord>[]
}, [hubVersion])
}, [hubVersion, i18n.locale])
const table = useReactTable({
data,
@@ -290,9 +295,11 @@ export default function SystemsTable({ filter }: { filter?: string }) {
getSortedRowModel: getSortedRowModel(),
onColumnFiltersChange: setColumnFilters,
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
state: {
sorting,
columnFilters,
columnVisibility,
},
defaultColumn: {
minSize: 0,
@@ -302,64 +309,102 @@ export default function SystemsTable({ filter }: { filter?: string }) {
})
return (
<div className="rounded-md border overflow-hidden">
<Table>
<TableHeader className="bg-muted/40">
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead className="px-2" key={header.id}>
{header.isPlaceholder
? null
: flexRender(header.column.columnDef.header, header.getContext())}
</TableHead>
)
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.original.id}
data-state={row.getIsSelected() && 'selected'}
className={cn('cursor-pointer transition-opacity', {
'opacity-50': row.original.status === 'paused',
})}
onClick={(e) => {
const target = e.target as HTMLElement
if (!target.closest('[data-nolink]') && e.currentTarget.contains(target)) {
navigate(`/system/${encodeURIComponent(row.original.name)}`)
}
}}
>
{row.getVisibleCells().map((cell) => (
<TableCell
key={cell.id}
style={{
width:
cell.column.getSize() === Number.MAX_SAFE_INTEGER
? 'auto'
: cell.column.getSize(),
<Card>
<CardHeader className="pb-5 px-2 sm:px-6 max-sm:pt-5 max-sm:pb-1">
<div className="grid md:flex gap-5 w-full items-end">
<div className="px-2 sm:px-1">
<CardTitle className="mb-2.5">
<Trans>All Systems</Trans>
</CardTitle>
<CardDescription>
<Trans>Updated in real time. Click on a system to view information.</Trans>
</CardDescription>
</div>
<div className="flex gap-2 ms-auto w-full md:w-80">
<Input placeholder={t`Filter...`} onChange={(e) => setFilter(e.target.value)} className="px-4" />
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline">
<Trans comment="Context: table columns">Columns</Trans>{" "}
<ChevronDownIcon className="ms-1.5 h-4 w-4 opacity-90" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
{table
.getAllColumns()
.filter((column) => column.getCanHide())
.map((column) => {
return (
<DropdownMenuCheckboxItem
key={column.id}
checked={column.getIsVisible()}
onCheckedChange={(value) => column.toggleVisibility(!!value)}
>
{column.id}
</DropdownMenuCheckboxItem>
)
})}
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
</CardHeader>
<CardContent className="max-sm:p-2">
<div className="rounded-md border overflow-hidden">
<Table>
<TableHeader className="bg-muted/40">
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead className="px-2" key={header.id}>
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
</TableHead>
)
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.original.id}
data-state={row.getIsSelected() && "selected"}
className={cn("cursor-pointer transition-opacity", {
"opacity-50": row.original.status === "paused",
})}
onClick={(e) => {
const target = e.target as HTMLElement
if (!target.closest("[data-nolink]") && e.currentTarget.contains(target)) {
navigate(`/system/${encodeURIComponent(row.original.name)}`)
}
}}
className={cn('overflow-hidden relative', data.length > 10 ? 'py-2' : 'py-2.5')}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
{row.getVisibleCells().map((cell) => (
<TableCell
key={cell.id}
style={{
width: cell.column.getSize() === Number.MAX_SAFE_INTEGER ? "auto" : cell.column.getSize(),
}}
className={cn("overflow-hidden relative", data.length > 10 ? "py-2" : "py-2.5")}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
<Trans>No systems found.</Trans>
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
No systems found
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
</TableRow>
)}
</TableBody>
</Table>
</div>
</CardContent>
</Card>
)
}

View File

@@ -1,6 +1,6 @@
import { createContext, useContext, useEffect, useState } from 'react'
import { createContext, useContext, useEffect, useState } from "react"
type Theme = 'dark' | 'light' | 'system'
type Theme = "dark" | "light" | "system"
type ThemeProviderProps = {
children: React.ReactNode
@@ -14,7 +14,7 @@ type ThemeProviderState = {
}
const initialState: ThemeProviderState = {
theme: 'system',
theme: "system",
setTheme: () => null,
}
@@ -22,23 +22,19 @@ const ThemeProviderContext = createContext<ThemeProviderState>(initialState)
export function ThemeProvider({
children,
defaultTheme = 'system',
storageKey = 'ui-theme',
defaultTheme = "system",
storageKey = "ui-theme",
...props
}: ThemeProviderProps) {
const [theme, setTheme] = useState<Theme>(
() => (localStorage.getItem(storageKey) as Theme) || defaultTheme
)
const [theme, setTheme] = useState<Theme>(() => (localStorage.getItem(storageKey) as Theme) || defaultTheme)
useEffect(() => {
const root = window.document.documentElement
root.classList.remove('light', 'dark')
root.classList.remove("light", "dark")
if (theme === 'system') {
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light'
if (theme === "system") {
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"
root.classList.add(systemTheme)
return

View File

@@ -1,8 +1,8 @@
import * as React from 'react'
import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog'
import * as React from "react"
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
import { cn } from '@/lib/utils'
import { buttonVariants } from '@/components/ui/button'
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
const AlertDialog = AlertDialogPrimitive.Root
@@ -16,7 +16,7 @@ const AlertDialogOverlay = React.forwardRef<
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Overlay
className={cn(
'fixed inset-0 z-50 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
"fixed inset-0 z-50 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className
)}
{...props}
@@ -34,7 +34,7 @@ const AlertDialogContent = React.forwardRef<
<AlertDialogPrimitive.Content
ref={ref}
className={cn(
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
className
)}
{...props}
@@ -44,27 +44,20 @@ const AlertDialogContent = React.forwardRef<
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
const AlertDialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div className={cn('flex flex-col space-y-2 text-center sm:text-left', className)} {...props} />
<div className={cn("flex flex-col space-y-2 text-center sm:text-start", className)} {...props} />
)
AlertDialogHeader.displayName = 'AlertDialogHeader'
AlertDialogHeader.displayName = "AlertDialogHeader"
const AlertDialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', className)}
{...props}
/>
<div className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:gap-2", className)} {...props} />
)
AlertDialogFooter.displayName = 'AlertDialogFooter'
AlertDialogFooter.displayName = "AlertDialogFooter"
const AlertDialogTitle = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Title
ref={ref}
className={cn('text-lg font-semibold', className)}
{...props}
/>
<AlertDialogPrimitive.Title ref={ref} className={cn("text-lg font-semibold", className)} {...props} />
))
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
@@ -72,11 +65,7 @@ const AlertDialogDescription = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Description
ref={ref}
className={cn('text-sm text-muted-foreground', className)}
{...props}
/>
<AlertDialogPrimitive.Description ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />
))
AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName
@@ -94,7 +83,7 @@ const AlertDialogCancel = React.forwardRef<
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Cancel
ref={ref}
className={cn(buttonVariants({ variant: 'outline' }), 'mt-2 sm:mt-0', className)}
className={cn(buttonVariants({ variant: "outline" }), "mt-2 sm:mt-0", className)}
{...props}
/>
))

View File

@@ -1,10 +1,10 @@
import * as React from 'react'
import * as React from "react"
// import { cva, type VariantProps } from 'class-variance-authority'
import { cn } from '@/lib/utils'
import { cn } from "@/lib/utils"
// const alertVariants = cva(
// "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
// "relative w-full rounded-lg border p-4 [&>svg~*]:ps-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
// {
// variants: {
// variant: {
@@ -29,31 +29,26 @@ const Alert = React.forwardRef<
ref={ref}
role="alert"
className={cn(
'relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground bg-background text-foreground',
"relative w-full rounded-lg border p-4 [&>svg~*]:ps-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground bg-background text-foreground",
className
)}
{...props}
/>
))
Alert.displayName = 'Alert'
Alert.displayName = "Alert"
const AlertTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
({ className, ...props }, ref) => (
<h5
ref={ref}
className={cn('mb-1 -mt-0.5 font-medium leading-tight tracking-tight', className)}
{...props}
/>
<h5 ref={ref} className={cn("mb-1 -mt-0.5 font-medium leading-tight tracking-tight", className)} {...props} />
)
)
AlertTitle.displayName = 'AlertTitle'
AlertTitle.displayName = "AlertTitle"
const AlertDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={cn('text-sm [&_p]:leading-relaxed', className)} {...props} />
))
AlertDescription.displayName = 'AlertDescription'
const AlertDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
({ className, ...props }, ref) => (
<div ref={ref} className={cn("text-sm [&_p]:leading-relaxed", className)} {...props} />
)
)
AlertDescription.displayName = "AlertDescription"
export { Alert, AlertTitle, AlertDescription }

View File

@@ -4,33 +4,26 @@ import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const badgeVariants = cva(
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
{
variants: {
variant: {
default:
"border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
secondary:
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
destructive:
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
outline: "text-foreground",
},
},
defaultVariants: {
variant: "default",
},
}
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
{
variants: {
variant: {
default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
outline: "text-foreground",
},
},
defaultVariants: {
variant: "default",
},
}
)
export interface BadgeProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof badgeVariants> {}
export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {}
function Badge({ className, variant, ...props }: BadgeProps) {
return (
<div className={cn(badgeVariants({ variant }), className)} {...props} />
)
return <div className={cn(badgeVariants({ variant }), className)} {...props} />
}
export { Badge, badgeVariants }

View File

@@ -1,31 +1,31 @@
import * as React from 'react'
import { Slot } from '@radix-ui/react-slot'
import { cva, type VariantProps } from 'class-variance-authority'
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from '@/lib/utils'
import { cn } from "@/lib/utils"
const buttonVariants = cva(
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
icon: 'h-10 w-10',
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: 'default',
size: 'default',
variant: "default",
size: "default",
},
}
)
@@ -38,12 +38,10 @@ export interface ButtonProps
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : 'button'
return (
<Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />
)
const Comp = asChild ? Slot : "button"
return <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />
}
)
Button.displayName = 'Button'
Button.displayName = "Button"
export { Button, buttonVariants }

View File

@@ -2,78 +2,40 @@ import * as React from "react"
import { cn } from "@/lib/utils"
const Card = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn(
"rounded-lg border bg-card text-card-foreground shadow-sm",
className
)}
{...props}
/>
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
<div ref={ref} className={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)} {...props} />
))
Card.displayName = "Card"
const CardHeader = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex flex-col space-y-1.5 p-6", className)}
{...props}
/>
))
const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div ref={ref} className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />
)
)
CardHeader.displayName = "CardHeader"
const CardTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
<h3
ref={ref}
className={cn(
"text-2xl font-semibold leading-none tracking-tight",
className
)}
{...props}
/>
))
const CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
({ className, ...props }, ref) => (
<h3 ref={ref} className={cn("text-2xl font-semibold leading-none tracking-tight", className)} {...props} />
)
)
CardTitle.displayName = "CardTitle"
const CardDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<p
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
const CardDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
({ className, ...props }, ref) => (
<p ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />
)
)
CardDescription.displayName = "CardDescription"
const CardContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
))
const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
)
CardContent.displayName = "CardContent"
const CardFooter = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex items-center p-6 pt-0", className)}
{...props}
/>
))
const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => <div ref={ref} className={cn("flex items-center p-6 pt-0", className)} {...props} />
)
CardFooter.displayName = "CardFooter"
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }

View File

@@ -1,20 +1,17 @@
import * as React from 'react'
import * as RechartsPrimitive from 'recharts'
import * as React from "react"
import * as RechartsPrimitive from "recharts"
import { chartTimeData, cn } from '@/lib/utils'
import { ChartData } from '@/types'
import { chartTimeData, cn } from "@/lib/utils"
import { ChartData } from "@/types"
// Format: { THEME_NAME: CSS_SELECTOR }
const THEMES = { light: '', dark: '.dark' } as const
const THEMES = { light: "", dark: ".dark" } as const
export type ChartConfig = {
[k in string]: {
label?: React.ReactNode
icon?: React.ComponentType
} & (
| { color?: string; theme?: never }
| { color?: never; theme: Record<keyof typeof THEMES, string> }
)
} & ({ color?: string; theme?: never } | { color?: never; theme: Record<keyof typeof THEMES, string> })
}
// type ChartContextProps = {
@@ -35,13 +32,13 @@ export type ChartConfig = {
const ChartContainer = React.forwardRef<
HTMLDivElement,
React.ComponentProps<'div'> & {
React.ComponentProps<"div"> & {
// config: ChartConfig
children: React.ComponentProps<typeof RechartsPrimitive.ResponsiveContainer>['children']
children: React.ComponentProps<typeof RechartsPrimitive.ResponsiveContainer>["children"]
}
>(({ id, className, children, ...props }, ref) => {
const uniqueId = React.useId()
const chartId = `chart-${id || uniqueId.replace(/:/g, '')}`
const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`
return (
//<ChartContext.Provider value={{ config }}>
@@ -60,7 +57,7 @@ const ChartContainer = React.forwardRef<
//</ChartContext.Provider>
)
})
ChartContainer.displayName = 'Chart'
ChartContainer.displayName = "Chart"
// const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
// const colorConfig = Object.entries(config).filter(([_, config]) => config.theme || config.color)
@@ -94,9 +91,9 @@ const ChartTooltip = RechartsPrimitive.Tooltip
const ChartTooltipContent = React.forwardRef<
HTMLDivElement,
React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
React.ComponentProps<'div'> & {
React.ComponentProps<"div"> & {
hideLabel?: boolean
indicator?: 'line' | 'dot' | 'dashed'
indicator?: "line" | "dot" | "dashed"
nameKey?: string
labelKey?: string
unit?: string
@@ -109,7 +106,7 @@ const ChartTooltipContent = React.forwardRef<
active,
payload,
className,
indicator = 'line',
indicator = "line",
hideLabel = false,
label,
labelFormatter,
@@ -144,21 +141,19 @@ const ChartTooltipContent = React.forwardRef<
}
const [item] = payload
const key = `${labelKey || item.name || 'value'}`
const key = `${labelKey || item.name || "value"}`
const itemConfig = getPayloadConfigFromPayload(config, item, key)
const value = !labelKey && typeof label === 'string' ? label : itemConfig?.label
const value = !labelKey && typeof label === "string" ? label : itemConfig?.label
if (labelFormatter) {
return (
<div className={cn('font-medium', labelClassName)}>{labelFormatter(value, payload)}</div>
)
return <div className={cn("font-medium", labelClassName)}>{labelFormatter(value, payload)}</div>
}
if (!value) {
return null
}
return <div className={cn('font-medium', labelClassName)}>{value}</div>
return <div className={cn("font-medium", labelClassName)}>{value}</div>
}, [label, labelFormatter, payload, hideLabel, labelClassName, config, labelKey])
if (!active || !payload?.length) {
@@ -172,14 +167,14 @@ const ChartTooltipContent = React.forwardRef<
<div
ref={ref}
className={cn(
'grid min-w-[7rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl',
"grid min-w-[7rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl",
className
)}
>
{!nestLabel ? tooltipLabel : null}
<div className="grid gap-1.5">
{payload.map((item, index) => {
const key = `${nameKey || item.name || item.dataKey || 'value'}`
const key = `${nameKey || item.name || item.dataKey || "value"}`
const itemConfig = getPayloadConfigFromPayload(config, item, key)
const indicatorColor = color || item.payload.fill || item.color
@@ -187,8 +182,8 @@ const ChartTooltipContent = React.forwardRef<
<div
key={item?.name || item.dataKey}
className={cn(
'flex w-full items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground',
indicator === 'dot' && 'items-center'
"flex w-full items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground",
indicator === "dot" && "items-center"
)}
>
{formatter && item?.value !== undefined && item.name ? (
@@ -199,41 +194,35 @@ const ChartTooltipContent = React.forwardRef<
<itemConfig.icon />
) : (
<div
className={cn(
'shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]',
{
'h-2.5 w-2.5': indicator === 'dot',
'w-1': indicator === 'line',
'w-0 border-[1.5px] border-dashed bg-transparent':
indicator === 'dashed',
'my-0.5': nestLabel && indicator === 'dashed',
}
)}
className={cn("shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]", {
"h-2.5 w-2.5": indicator === "dot",
"w-1": indicator === "line",
"w-0 border-[1.5px] border-dashed bg-transparent": indicator === "dashed",
"my-0.5": nestLabel && indicator === "dashed",
})}
style={
{
'--color-bg': indicatorColor,
'--color-border': indicatorColor,
"--color-bg": indicatorColor,
"--color-border": indicatorColor,
} as React.CSSProperties
}
/>
)}
<div
className={cn(
'flex flex-1 justify-between leading-none gap-2',
nestLabel ? 'items-end' : 'items-center'
"flex flex-1 justify-between leading-none gap-2",
nestLabel ? "items-end" : "items-center"
)}
>
<div className="grid gap-1.5">
{nestLabel ? tooltipLabel : null}
<span className="text-muted-foreground">
{itemConfig?.label || item.name}
</span>
<span className="text-muted-foreground">{itemConfig?.label || item.name}</span>
</div>
{item.value !== undefined && (
<span className="font-medium tabular-nums text-foreground">
{content && typeof content === 'function'
{content && typeof content === "function"
? content(item, key)
: item.value.toLocaleString() + (unit ? unit : '')}
: item.value.toLocaleString() + (unit ? unit : "")}
</span>
)}
</div>
@@ -247,18 +236,18 @@ const ChartTooltipContent = React.forwardRef<
)
}
)
ChartTooltipContent.displayName = 'ChartTooltip'
ChartTooltipContent.displayName = "ChartTooltip"
const ChartLegend = RechartsPrimitive.Legend
const ChartLegendContent = React.forwardRef<
HTMLDivElement,
React.ComponentProps<'div'> &
Pick<RechartsPrimitive.LegendProps, 'payload' | 'verticalAlign'> & {
React.ComponentProps<"div"> &
Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
hideIcon?: boolean
nameKey?: string
}
>(({ className, payload, verticalAlign = 'bottom' }, ref) => {
>(({ className, payload, verticalAlign = "bottom" }, ref) => {
// const { config } = useChart()
if (!payload?.length) {
@@ -269,8 +258,8 @@ const ChartLegendContent = React.forwardRef<
<div
ref={ref}
className={cn(
'flex items-center justify-center gap-4 gap-y-1 flex-wrap',
verticalAlign === 'top' ? 'pb-3' : 'pt-3',
"flex items-center justify-center gap-4 gap-y-1 flex-wrap",
verticalAlign === "top" ? "pb-3" : "pt-3",
className
)}
>
@@ -283,7 +272,7 @@ const ChartLegendContent = React.forwardRef<
key={item.value}
className={cn(
// 'flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground text-muted-foreground'
'flex items-center gap-1.5 text-muted-foreground'
"flex items-center gap-1.5 text-muted-foreground"
)}
>
{/* {itemConfig?.icon && !hideIcon ? (
@@ -304,27 +293,27 @@ const ChartLegendContent = React.forwardRef<
</div>
)
})
ChartLegendContent.displayName = 'ChartLegend'
ChartLegendContent.displayName = "ChartLegend"
// Helper to extract item config from a payload.
function getPayloadConfigFromPayload(config: ChartConfig, payload: unknown, key: string) {
if (typeof payload !== 'object' || payload === null) {
if (typeof payload !== "object" || payload === null) {
return undefined
}
const payloadPayload =
'payload' in payload && typeof payload.payload === 'object' && payload.payload !== null
"payload" in payload && typeof payload.payload === "object" && payload.payload !== null
? payload.payload
: undefined
let configLabelKey: string = key
if (key in payload && typeof payload[key as keyof typeof payload] === 'string') {
if (key in payload && typeof payload[key as keyof typeof payload] === "string") {
configLabelKey = payload[key as keyof typeof payload] as string
} else if (
payloadPayload &&
key in payloadPayload &&
typeof payloadPayload[key as keyof typeof payloadPayload] === 'string'
typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
) {
configLabelKey = payloadPayload[key as keyof typeof payloadPayload] as string
}

View File

@@ -1,8 +1,8 @@
import * as React from 'react'
import * as CheckboxPrimitive from '@radix-ui/react-checkbox'
import { Check } from 'lucide-react'
import * as React from "react"
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
import { Check } from "lucide-react"
import { cn } from '@/lib/utils'
import { cn } from "@/lib/utils"
const Checkbox = React.forwardRef<
React.ElementRef<typeof CheckboxPrimitive.Root>,
@@ -11,12 +11,12 @@ const Checkbox = React.forwardRef<
<CheckboxPrimitive.Root
ref={ref}
className={cn(
'peer h-4 w-4 shrink-0 rounded-[.3em] border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground',
"peer h-4 w-4 shrink-0 rounded-[.3em] border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
className
)}
{...props}
>
<CheckboxPrimitive.Indicator className={cn('flex items-center justify-center text-current')}>
<CheckboxPrimitive.Indicator className={cn("flex items-center justify-center text-current")}>
<Check className="h-4 w-4" />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>

View File

@@ -1,10 +1,10 @@
import * as React from 'react'
import { DialogTitle, type DialogProps } from '@radix-ui/react-dialog'
import { Command as CommandPrimitive } from 'cmdk'
import { Search } from 'lucide-react'
import * as React from "react"
import { DialogTitle, type DialogProps } from "@radix-ui/react-dialog"
import { Command as CommandPrimitive } from "cmdk"
import { Search } from "lucide-react"
import { cn } from '@/lib/utils'
import { Dialog, DialogContent } from '@/components/ui/dialog'
import { cn } from "@/lib/utils"
import { Dialog, DialogContent } from "@/components/ui/dialog"
const Command = React.forwardRef<
React.ElementRef<typeof CommandPrimitive>,
@@ -12,10 +12,7 @@ const Command = React.forwardRef<
>(({ className, ...props }, ref) => (
<CommandPrimitive
ref={ref}
className={cn(
'flex h-full w-full flex-col overflow-hidden bg-popover text-popover-foreground',
className
)}
className={cn("flex h-full w-full flex-col overflow-hidden bg-popover text-popover-foreground", className)}
{...props}
/>
))
@@ -43,11 +40,11 @@ const CommandInput = React.forwardRef<
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
>(({ className, ...props }, ref) => (
<div className="flex items-center border-b px-3" cmdk-input-wrapper="">
<Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
<Search className="me-2 h-4 w-4 shrink-0 opacity-50" />
<CommandPrimitive.Input
ref={ref}
className={cn(
'flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50',
"flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...props}
@@ -63,7 +60,7 @@ const CommandList = React.forwardRef<
>(({ className, ...props }, ref) => (
<CommandPrimitive.List
ref={ref}
className={cn('max-h-[300px] overflow-y-auto overflow-x-hidden', className)}
className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)}
{...props}
/>
))
@@ -73,9 +70,7 @@ CommandList.displayName = CommandPrimitive.List.displayName
const CommandEmpty = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Empty>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
>((props, ref) => (
<CommandPrimitive.Empty ref={ref} className="py-6 text-center text-sm" {...props} />
))
>((props, ref) => <CommandPrimitive.Empty ref={ref} className="py-6 text-center text-sm" {...props} />)
CommandEmpty.displayName = CommandPrimitive.Empty.displayName
@@ -86,7 +81,7 @@ const CommandGroup = React.forwardRef<
<CommandPrimitive.Group
ref={ref}
className={cn(
'overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground',
"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
className
)}
{...props}
@@ -99,11 +94,7 @@ const CommandSeparator = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Separator
ref={ref}
className={cn('-mx-1 h-px bg-border', className)}
{...props}
/>
<CommandPrimitive.Separator ref={ref} className={cn("-mx-1 h-px bg-border", className)} {...props} />
))
CommandSeparator.displayName = CommandPrimitive.Separator.displayName
@@ -124,14 +115,9 @@ const CommandItem = React.forwardRef<
CommandItem.displayName = CommandPrimitive.Item.displayName
const CommandShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
return (
<span
className={cn('ml-auto text-xs tracking-wide text-muted-foreground', className)}
{...props}
/>
)
return <span className={cn("ms-auto text-xs tracking-wide text-muted-foreground", className)} {...props} />
}
CommandShortcut.displayName = 'CommandShortcut'
CommandShortcut.displayName = "CommandShortcut"
export {
Command,

View File

@@ -1,8 +1,8 @@
import * as React from 'react'
import * as DialogPrimitive from '@radix-ui/react-dialog'
import { X } from 'lucide-react'
import * as React from "react"
import * as DialogPrimitive from "@radix-ui/react-dialog"
import { X } from "lucide-react"
import { cn } from '@/lib/utils'
import { cn } from "@/lib/utils"
const Dialog = DialogPrimitive.Root
@@ -19,7 +19,7 @@ const DialogOverlay = React.forwardRef<
<DialogPrimitive.Overlay
ref={ref}
className={cn(
'fixed inset-0 z-50 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
"fixed inset-0 z-50 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className
)}
{...props}
@@ -36,13 +36,13 @@ const DialogContent = React.forwardRef<
<DialogPrimitive.Content
ref={ref}
className={cn(
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-card p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-card p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
className
)}
{...props}
>
{children}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<DialogPrimitive.Close className="absolute end-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
@@ -52,17 +52,14 @@ const DialogContent = React.forwardRef<
DialogContent.displayName = DialogPrimitive.Content.displayName
const DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div className={cn('flex flex-col space-y-1.5 text-center sm:text-left', className)} {...props} />
<div className={cn("flex flex-col space-y-1.5 text-center sm:text-start", className)} {...props} />
)
DialogHeader.displayName = 'DialogHeader'
DialogHeader.displayName = "DialogHeader"
const DialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', className)}
{...props}
/>
<div className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:gap-3.5", className)} {...props} />
)
DialogFooter.displayName = 'DialogFooter'
DialogFooter.displayName = "DialogFooter"
const DialogTitle = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Title>,
@@ -70,7 +67,7 @@ const DialogTitle = React.forwardRef<
>(({ className, ...props }, ref) => (
<DialogPrimitive.Title
ref={ref}
className={cn('text-lg font-semibold leading-none tracking-tight', className)}
className={cn("text-lg font-semibold leading-none tracking-tight", className)}
{...props}
/>
))
@@ -80,11 +77,7 @@ const DialogDescription = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Description
ref={ref}
className={cn('text-sm text-muted-foreground', className)}
{...props}
/>
<DialogPrimitive.Description ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />
))
DialogDescription.displayName = DialogPrimitive.Description.displayName

View File

@@ -17,182 +17,163 @@ const DropdownMenuSub = DropdownMenuPrimitive.Sub
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
const DropdownMenuSubTrigger = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
inset?: boolean
}
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
inset?: boolean
}
>(({ className, inset, children, ...props }, ref) => (
<DropdownMenuPrimitive.SubTrigger
ref={ref}
className={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",
inset && "pl-8",
className
)}
{...props}
>
{children}
<ChevronRight className="ml-auto h-4 w-4" />
</DropdownMenuPrimitive.SubTrigger>
<DropdownMenuPrimitive.SubTrigger
ref={ref}
className={cn(
"flex cursor-default select-none items-center rounded-sm px-2.5 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",
inset && "ps-8",
className
)}
{...props}
>
{children}
<ChevronRight className="ms-auto h-4 w-4" />
</DropdownMenuPrimitive.SubTrigger>
))
DropdownMenuSubTrigger.displayName =
DropdownMenuPrimitive.SubTrigger.displayName
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName
const DropdownMenuSubContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
>(({ className, ...props }, ref) => (
<DropdownMenuPrimitive.SubContent
ref={ref}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
/>
<DropdownMenuPrimitive.SubContent
ref={ref}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
/>
))
DropdownMenuSubContent.displayName =
DropdownMenuPrimitive.SubContent.displayName
DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName
const DropdownMenuContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
/>
</DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
/>
</DropdownMenuPrimitive.Portal>
))
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
const DropdownMenuItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean
}
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean
}
>(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Item
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
inset && "pl-8",
className
)}
{...props}
/>
<DropdownMenuPrimitive.Item
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2.5 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
inset && "ps-8",
className
)}
{...props}
/>
))
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
const DropdownMenuCheckboxItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
>(({ className, children, checked, ...props }, ref) => (
<DropdownMenuPrimitive.CheckboxItem
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
checked={checked}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.CheckboxItem>
<DropdownMenuPrimitive.CheckboxItem
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 ps-8 pe-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
checked={checked}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.CheckboxItem>
))
DropdownMenuCheckboxItem.displayName =
DropdownMenuPrimitive.CheckboxItem.displayName
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName
const DropdownMenuRadioItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
>(({ className, children, ...props }, ref) => (
<DropdownMenuPrimitive.RadioItem
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Circle className="h-2 w-2 fill-current" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.RadioItem>
<DropdownMenuPrimitive.RadioItem
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 ps-8 pe-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Circle className="h-2 w-2 fill-current" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.RadioItem>
))
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
const DropdownMenuLabel = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
inset?: boolean
}
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
inset?: boolean
}
>(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Label
ref={ref}
className={cn(
"px-2 py-1.5 text-sm font-semibold",
inset && "pl-8",
className
)}
{...props}
/>
<DropdownMenuPrimitive.Label
ref={ref}
className={cn("px-2.5 py-1.5 text-sm font-semibold", inset && "ps-8", className)}
{...props}
/>
))
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
const DropdownMenuSeparator = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
>(({ className, ...props }, ref) => (
<DropdownMenuPrimitive.Separator
ref={ref}
className={cn("-mx-1 my-1 h-px bg-muted", className)}
{...props}
/>
<DropdownMenuPrimitive.Separator ref={ref} className={cn("-mx-1 my-1 h-px bg-muted", className)} {...props} />
))
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
const DropdownMenuShortcut = ({
className,
...props
}: React.HTMLAttributes<HTMLSpanElement>) => {
return (
<span
className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
{...props}
/>
)
const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
return <span className={cn("ms-auto text-xs tracking-widest opacity-60", className)} {...props} />
}
DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
export {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuCheckboxItem,
DropdownMenuRadioItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuGroup,
DropdownMenuPortal,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuRadioGroup,
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuCheckboxItem,
DropdownMenuRadioItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuGroup,
DropdownMenuPortal,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuRadioGroup,
}

View File

@@ -1,4 +1,4 @@
import { SVGProps } from 'react'
import { SVGProps } from "react"
// linux-logo-bold from https://github.com/phosphor-icons/core (MIT license)
export function TuxIcon(props: SVGProps<SVGSVGElement>) {
@@ -49,14 +49,7 @@ export function ChartMax(props: SVGProps<SVGSVGElement>) {
// Lucide https://github.com/lucide-icons/lucide (not in package for some reason)
export function EthernetIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeWidth="2"
viewBox="0 0 24 24"
{...props}
>
<svg fill="none" stroke="currentColor" strokeLinecap="round" strokeWidth="2" viewBox="0 0 24 24" {...props}>
<path d="m15 20 3-3h2a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h2l3 3zM6 8v1m4-1v1m4-1v1m4-1v1" />
</svg>
)

View File

@@ -1,27 +1,24 @@
import * as React from 'react'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { XIcon } from 'lucide-react'
import { type InputProps } from './input'
import { cn } from '@/lib/utils'
import * as React from "react"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { XIcon } from "lucide-react"
import { type InputProps } from "./input"
import { cn } from "@/lib/utils"
type InputTagsProps = Omit<InputProps, 'value' | 'onChange'> & {
type InputTagsProps = Omit<InputProps, "value" | "onChange"> & {
value: string[]
onChange: React.Dispatch<React.SetStateAction<string[]>>
}
const InputTags = React.forwardRef<HTMLInputElement, InputTagsProps>(
({ className, value, onChange, ...props }, ref) => {
const [pendingDataPoint, setPendingDataPoint] = React.useState('')
const [pendingDataPoint, setPendingDataPoint] = React.useState("")
React.useEffect(() => {
if (pendingDataPoint.includes(',')) {
const newDataPoints = new Set([
...value,
...pendingDataPoint.split(',').map((chunk) => chunk.trim()),
])
if (pendingDataPoint.includes(",")) {
const newDataPoints = new Set([...value, ...pendingDataPoint.split(",").map((chunk) => chunk.trim())])
onChange(Array.from(newDataPoints))
setPendingDataPoint('')
setPendingDataPoint("")
}
}, [pendingDataPoint, onChange, value])
@@ -29,14 +26,14 @@ const InputTags = React.forwardRef<HTMLInputElement, InputTagsProps>(
if (pendingDataPoint) {
const newDataPoints = new Set([...value, pendingDataPoint])
onChange(Array.from(newDataPoints))
setPendingDataPoint('')
setPendingDataPoint("")
}
}
return (
<div
className={cn(
'bg-background min-h-10 flex w-full flex-wrap gap-2 rounded-md border border-input px-3 py-2 text-sm placeholder:text-muted-foreground has-[:focus-visible]:outline-none ring-offset-background has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-ring has-[:focus-visible]:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
"bg-background min-h-10 flex w-full flex-wrap gap-2 rounded-md border border-input px-3 py-2 text-sm placeholder:text-muted-foreground has-[:focus-visible]:outline-none ring-offset-background has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-ring has-[:focus-visible]:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
>
@@ -46,7 +43,7 @@ const InputTags = React.forwardRef<HTMLInputElement, InputTagsProps>(
<Button
variant="ghost"
size="icon"
className="ml-2 h-3 w-3"
className="ms-2 h-3 w-3"
onClick={() => {
onChange(value.filter((i) => i !== item))
}}
@@ -60,10 +57,10 @@ const InputTags = React.forwardRef<HTMLInputElement, InputTagsProps>(
value={pendingDataPoint}
onChange={(e) => setPendingDataPoint(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ',') {
if (e.key === "Enter" || e.key === ",") {
e.preventDefault()
addPendingDataPoint()
} else if (e.key === 'Backspace' && pendingDataPoint.length === 0 && value.length > 0) {
} else if (e.key === "Backspace" && pendingDataPoint.length === 0 && value.length > 0) {
e.preventDefault()
onChange(value.slice(0, -1))
}
@@ -76,6 +73,6 @@ const InputTags = React.forwardRef<HTMLInputElement, InputTagsProps>(
}
)
InputTags.displayName = 'InputTags'
InputTags.displayName = "InputTags"
export { InputTags }

View File

@@ -2,24 +2,21 @@ import * as React from "react"
import { cn } from "@/lib/utils"
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {}
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}
const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props}
/>
)
}
)
const Input = React.forwardRef<HTMLInputElement, InputProps>(({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props}
/>
)
})
Input.displayName = "Input"
export { Input }

View File

@@ -4,20 +4,13 @@ import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
)
const labelVariants = cva("text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70")
const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
VariantProps<typeof labelVariants>
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => (
<LabelPrimitive.Root
ref={ref}
className={cn(labelVariants(), className)}
{...props}
/>
<LabelPrimitive.Root ref={ref} className={cn(labelVariants(), className)} {...props} />
))
Label.displayName = LabelPrimitive.Root.displayName

View File

@@ -11,148 +11,133 @@ const SelectGroup = SelectPrimitive.Group
const SelectValue = SelectPrimitive.Value
const SelectTrigger = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
React.ElementRef<typeof SelectPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Trigger
ref={ref}
className={cn(
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
className
)}
{...props}
>
{children}
<SelectPrimitive.Icon asChild>
<ChevronDown className="h-4 w-4 opacity-50" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
<SelectPrimitive.Trigger
ref={ref}
className={cn(
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
className
)}
{...props}
>
{children}
<SelectPrimitive.Icon asChild>
<ChevronDown className="h-4 w-4 opacity-50" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
))
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
const SelectScrollUpButton = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
>(({ className, ...props }, ref) => (
<SelectPrimitive.ScrollUpButton
ref={ref}
className={cn(
"flex cursor-default items-center justify-center py-1",
className
)}
{...props}
>
<ChevronUp className="h-4 w-4" />
</SelectPrimitive.ScrollUpButton>
<SelectPrimitive.ScrollUpButton
ref={ref}
className={cn("flex cursor-default items-center justify-center py-1", className)}
{...props}
>
<ChevronUp className="h-4 w-4" />
</SelectPrimitive.ScrollUpButton>
))
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
const SelectScrollDownButton = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
>(({ className, ...props }, ref) => (
<SelectPrimitive.ScrollDownButton
ref={ref}
className={cn(
"flex cursor-default items-center justify-center py-1",
className
)}
{...props}
>
<ChevronDown className="h-4 w-4" />
</SelectPrimitive.ScrollDownButton>
<SelectPrimitive.ScrollDownButton
ref={ref}
className={cn("flex cursor-default items-center justify-center py-1", className)}
{...props}
>
<ChevronDown className="h-4 w-4" />
</SelectPrimitive.ScrollDownButton>
))
SelectScrollDownButton.displayName =
SelectPrimitive.ScrollDownButton.displayName
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName
const SelectContent = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
React.ElementRef<typeof SelectPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
>(({ className, children, position = "popper", ...props }, ref) => (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
ref={ref}
className={cn(
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className
)}
position={position}
{...props}
>
<SelectScrollUpButton />
<SelectPrimitive.Viewport
className={cn(
"p-1",
position === "popper" &&
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
)}
>
{children}
</SelectPrimitive.Viewport>
<SelectScrollDownButton />
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
<SelectPrimitive.Portal>
<SelectPrimitive.Content
ref={ref}
className={cn(
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className
)}
position={position}
{...props}
>
<SelectScrollUpButton />
<SelectPrimitive.Viewport
className={cn(
"p-1",
position === "popper" &&
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
)}
>
{children}
</SelectPrimitive.Viewport>
<SelectScrollDownButton />
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
))
SelectContent.displayName = SelectPrimitive.Content.displayName
const SelectLabel = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
React.ElementRef<typeof SelectPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
>(({ className, ...props }, ref) => (
<SelectPrimitive.Label
ref={ref}
className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
{...props}
/>
<SelectPrimitive.Label ref={ref} className={cn("py-1.5 ps-8 pe-2 text-sm font-semibold", className)} {...props} />
))
SelectLabel.displayName = SelectPrimitive.Label.displayName
const SelectItem = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
React.ElementRef<typeof SelectPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Item
ref={ref}
className={cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
</SelectPrimitive.ItemIndicator>
</span>
<SelectPrimitive.Item
ref={ref}
className={cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 ps-8 pe-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
</SelectPrimitive.ItemIndicator>
</span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
))
SelectItem.displayName = SelectPrimitive.Item.displayName
const SelectSeparator = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
React.ElementRef<typeof SelectPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
>(({ className, ...props }, ref) => (
<SelectPrimitive.Separator
ref={ref}
className={cn("-mx-1 my-1 h-px bg-muted", className)}
{...props}
/>
<SelectPrimitive.Separator ref={ref} className={cn("-mx-1 my-1 h-px bg-muted", className)} {...props} />
))
SelectSeparator.displayName = SelectPrimitive.Separator.displayName
export {
Select,
SelectGroup,
SelectValue,
SelectTrigger,
SelectContent,
SelectLabel,
SelectItem,
SelectSeparator,
SelectScrollUpButton,
SelectScrollDownButton,
Select,
SelectGroup,
SelectValue,
SelectTrigger,
SelectContent,
SelectLabel,
SelectItem,
SelectSeparator,
SelectScrollUpButton,
SelectScrollDownButton,
}

View File

@@ -4,26 +4,17 @@ import * as SeparatorPrimitive from "@radix-ui/react-separator"
import { cn } from "@/lib/utils"
const Separator = React.forwardRef<
React.ElementRef<typeof SeparatorPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
>(
(
{ className, orientation = "horizontal", decorative = true, ...props },
ref
) => (
<SeparatorPrimitive.Root
ref={ref}
decorative={decorative}
orientation={orientation}
className={cn(
"shrink-0 bg-border",
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
className
)}
{...props}
/>
)
)
React.ElementRef<typeof SeparatorPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
>(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => (
<SeparatorPrimitive.Root
ref={ref}
decorative={decorative}
orientation={orientation}
className={cn("shrink-0 bg-border", orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]", className)}
{...props}
/>
))
Separator.displayName = SeparatorPrimitive.Root.displayName
export { Separator }

View File

@@ -1,7 +1,7 @@
import * as React from 'react'
import * as SliderPrimitive from '@radix-ui/react-slider'
import * as React from "react"
import * as SliderPrimitive from "@radix-ui/react-slider"
import { cn } from '@/lib/utils'
import { cn } from "@/lib/utils"
const Slider = React.forwardRef<
React.ElementRef<typeof SliderPrimitive.Root>,
@@ -9,7 +9,7 @@ const Slider = React.forwardRef<
>(({ className, ...props }, ref) => (
<SliderPrimitive.Root
ref={ref}
className={cn('relative flex w-full touch-none select-none items-center', className)}
className={cn("relative flex w-full touch-none select-none items-center", className)}
{...props}
>
<SliderPrimitive.Track className="relative h-2 w-full grow overflow-hidden rounded-full bg-secondary">

View File

@@ -4,23 +4,23 @@ import * as SwitchPrimitives from "@radix-ui/react-switch"
import { cn } from "@/lib/utils"
const Switch = React.forwardRef<
React.ElementRef<typeof SwitchPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
React.ElementRef<typeof SwitchPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
>(({ className, ...props }, ref) => (
<SwitchPrimitives.Root
className={cn(
"peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
className
)}
{...props}
ref={ref}
>
<SwitchPrimitives.Thumb
className={cn(
"pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0"
)}
/>
</SwitchPrimitives.Root>
<SwitchPrimitives.Root
className={cn(
"peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
className
)}
{...props}
ref={ref}
>
<SwitchPrimitives.Thumb
className={cn(
"pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 rtl:data-[state=checked]:-translate-x-5 data-[state=unchecked]:translate-x-0"
)}
/>
</SwitchPrimitives.Root>
))
Switch.displayName = SwitchPrimitives.Root.displayName

View File

@@ -1,91 +1,72 @@
import * as React from 'react'
import * as React from "react"
import { cn } from '@/lib/utils'
import { cn } from "@/lib/utils"
const Table = React.forwardRef<HTMLTableElement, React.HTMLAttributes<HTMLTableElement>>(
({ className, ...props }, ref) => (
<div className="relative w-full overflow-auto">
<table ref={ref} className={cn('w-full caption-bottom text-sm', className)} {...props} />
<table ref={ref} className={cn("w-full caption-bottom text-sm", className)} {...props} />
</div>
)
)
Table.displayName = 'Table'
Table.displayName = "Table"
const TableHeader = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead ref={ref} className={cn('[&_tr]:border-b', className)} {...props} />
))
TableHeader.displayName = 'TableHeader'
const TableHeader = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
({ className, ...props }, ref) => <thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
)
TableHeader.displayName = "TableHeader"
const TableBody = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tbody ref={ref} className={cn('[&_tr:last-child]:border-0', className)} {...props} />
))
TableBody.displayName = 'TableBody'
const TableBody = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
({ className, ...props }, ref) => (
<tbody ref={ref} className={cn("[&_tr:last-child]:border-0", className)} {...props} />
)
)
TableBody.displayName = "TableBody"
const TableFooter = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tfoot
ref={ref}
className={cn('border-t bg-muted/50 font-medium [&>tr]:last:border-b-0', className)}
{...props}
/>
))
TableFooter.displayName = 'TableFooter'
const TableFooter = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
({ className, ...props }, ref) => (
<tfoot ref={ref} className={cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", className)} {...props} />
)
)
TableFooter.displayName = "TableFooter"
const TableRow = React.forwardRef<HTMLTableRowElement, React.HTMLAttributes<HTMLTableRowElement>>(
({ className, ...props }, ref) => (
<tr
ref={ref}
className={cn("border-b hover:bg-muted/40 dark:hover:bg-muted/30 data-[state=selected]:bg-muted", className)}
{...props}
/>
)
)
TableRow.displayName = "TableRow"
const TableHead = React.forwardRef<HTMLTableCellElement, React.ThHTMLAttributes<HTMLTableCellElement>>(
({ className, ...props }, ref) => (
<th
ref={ref}
className={cn(
'border-b hover:bg-muted/40 dark:hover:bg-muted/30 data-[state=selected]:bg-muted',
"h-12 px-4 text-start align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pe-0",
className
)}
{...props}
/>
)
)
TableRow.displayName = 'TableRow'
TableHead.displayName = "TableHead"
const TableHead = React.forwardRef<
HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<th
ref={ref}
className={cn(
'h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0',
className
)}
{...props}
/>
))
TableHead.displayName = 'TableHead'
const TableCell = React.forwardRef<HTMLTableCellElement, React.TdHTMLAttributes<HTMLTableCellElement>>(
({ className, ...props }, ref) => (
<td ref={ref} className={cn("p-4 align-middle [&:has([role=checkbox])]:pe-0", className)} {...props} />
)
)
TableCell.displayName = "TableCell"
const TableCell = React.forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<td
ref={ref}
className={cn('p-4 align-middle [&:has([role=checkbox])]:pr-0', className)}
{...props}
/>
))
TableCell.displayName = 'TableCell'
const TableCaption = React.forwardRef<
HTMLTableCaptionElement,
React.HTMLAttributes<HTMLTableCaptionElement>
>(({ className, ...props }, ref) => (
<caption ref={ref} className={cn('mt-4 text-sm text-muted-foreground', className)} {...props} />
))
TableCaption.displayName = 'TableCaption'
const TableCaption = React.forwardRef<HTMLTableCaptionElement, React.HTMLAttributes<HTMLTableCaptionElement>>(
({ className, ...props }, ref) => (
<caption ref={ref} className={cn("mt-4 text-sm text-muted-foreground", className)} {...props} />
)
)
TableCaption.displayName = "TableCaption"
export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption }

View File

@@ -6,47 +6,47 @@ import { cn } from "@/lib/utils"
const Tabs = TabsPrimitive.Root
const TabsList = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.List>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
React.ElementRef<typeof TabsPrimitive.List>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
>(({ className, ...props }, ref) => (
<TabsPrimitive.List
ref={ref}
className={cn(
"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
className
)}
{...props}
/>
<TabsPrimitive.List
ref={ref}
className={cn(
"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
className
)}
{...props}
/>
))
TabsList.displayName = TabsPrimitive.List.displayName
const TabsTrigger = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
React.ElementRef<typeof TabsPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Trigger
ref={ref}
className={cn(
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
className
)}
{...props}
/>
<TabsPrimitive.Trigger
ref={ref}
className={cn(
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
className
)}
{...props}
/>
))
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
const TabsContent = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
React.ElementRef<typeof TabsPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Content
ref={ref}
className={cn(
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
className
)}
{...props}
/>
<TabsPrimitive.Content
ref={ref}
className={cn(
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
className
)}
{...props}
/>
))
TabsContent.displayName = TabsPrimitive.Content.displayName

View File

@@ -1,23 +1,21 @@
import * as React from 'react'
import * as React from "react"
import { cn } from '@/lib/utils'
import { cn } from "@/lib/utils"
export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
({ className, ...props }, ref) => {
return (
<textarea
className={cn(
'flex min-h-14 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
className
)}
ref={ref}
{...props}
/>
)
}
)
Textarea.displayName = 'Textarea'
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(({ className, ...props }, ref) => {
return (
<textarea
className={cn(
"flex min-h-14 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props}
/>
)
})
Textarea.displayName = "Textarea"
export { Textarea }

View File

@@ -1,9 +1,9 @@
import * as React from 'react'
import * as ToastPrimitives from '@radix-ui/react-toast'
import { cva, type VariantProps } from 'class-variance-authority'
import { X } from 'lucide-react'
import * as React from "react"
import * as ToastPrimitives from "@radix-ui/react-toast"
import { cva, type VariantProps } from "class-variance-authority"
import { X } from "lucide-react"
import { cn } from '@/lib/utils'
import { cn } from "@/lib/utils"
const ToastProvider = ToastPrimitives.Provider
@@ -14,7 +14,7 @@ const ToastViewport = React.forwardRef<
<ToastPrimitives.Viewport
ref={ref}
className={cn(
'fixed top-0 z-[100] flex max-h-dvh w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]',
"fixed top-0 z-[100] flex max-h-dvh w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
className
)}
{...props}
@@ -23,17 +23,16 @@ const ToastViewport = React.forwardRef<
ToastViewport.displayName = ToastPrimitives.Viewport.displayName
const toastVariants = cva(
'group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full',
"group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pe-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
{
variants: {
variant: {
default: 'border bg-background text-foreground',
destructive:
'destructive group border-destructive bg-destructive text-destructive-foreground',
default: "border bg-background text-foreground",
destructive: "destructive group border-destructive bg-destructive text-destructive-foreground",
},
},
defaultVariants: {
variant: 'default',
variant: "default",
},
}
)
@@ -42,13 +41,7 @@ const Toast = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> & VariantProps<typeof toastVariants>
>(({ className, variant, ...props }, ref) => {
return (
<ToastPrimitives.Root
ref={ref}
className={cn(toastVariants({ variant }), className)}
{...props}
/>
)
return <ToastPrimitives.Root ref={ref} className={cn(toastVariants({ variant }), className)} {...props} />
})
Toast.displayName = ToastPrimitives.Root.displayName
@@ -59,7 +52,7 @@ const ToastAction = React.forwardRef<
<ToastPrimitives.Action
ref={ref}
className={cn(
'inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive',
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
className
)}
{...props}
@@ -74,7 +67,7 @@ const ToastClose = React.forwardRef<
<ToastPrimitives.Close
ref={ref}
className={cn(
'absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600',
"absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
className
)}
toast-close=""
@@ -89,7 +82,7 @@ const ToastTitle = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Title>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Title ref={ref} className={cn('text-sm font-semibold', className)} {...props} />
<ToastPrimitives.Title ref={ref} className={cn("text-sm font-semibold", className)} {...props} />
))
ToastTitle.displayName = ToastPrimitives.Title.displayName
@@ -97,11 +90,7 @@ const ToastDescription = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Description>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Description
ref={ref}
className={cn('text-sm opacity-90', className)}
{...props}
/>
<ToastPrimitives.Description ref={ref} className={cn("text-sm opacity-90", className)} {...props} />
))
ToastDescription.displayName = ToastPrimitives.Description.displayName

View File

@@ -1,33 +1,24 @@
import {
Toast,
ToastClose,
ToastDescription,
ToastProvider,
ToastTitle,
ToastViewport,
} from "@/components/ui/toast"
import { Toast, ToastClose, ToastDescription, ToastProvider, ToastTitle, ToastViewport } from "@/components/ui/toast"
import { useToast } from "@/components/ui/use-toast"
export function Toaster() {
const { toasts } = useToast()
const { toasts } = useToast()
return (
<ToastProvider>
{toasts.map(function ({ id, title, description, action, ...props }) {
return (
<Toast key={id} {...props}>
<div className="grid gap-1">
{title && <ToastTitle>{title}</ToastTitle>}
{description && (
<ToastDescription>{description}</ToastDescription>
)}
</div>
{action}
<ToastClose />
</Toast>
)
})}
<ToastViewport />
</ToastProvider>
)
return (
<ToastProvider>
{toasts.map(function ({ id, title, description, action, ...props }) {
return (
<Toast key={id} {...props}>
<div className="grid gap-1">
{title && <ToastTitle>{title}</ToastTitle>}
{description && <ToastDescription>{description}</ToastDescription>}
</div>
{action}
<ToastClose />
</Toast>
)
})}
<ToastViewport />
</ToastProvider>
)
}

View File

@@ -1,7 +1,7 @@
import * as React from 'react'
import * as TooltipPrimitive from '@radix-ui/react-tooltip'
import * as React from "react"
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
import { cn } from '@/lib/utils'
import { cn } from "@/lib/utils"
const TooltipProvider = TooltipPrimitive.Provider
@@ -17,7 +17,7 @@ const TooltipContent = React.forwardRef<
ref={ref}
sideOffset={sideOffset}
className={cn(
'z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}

View File

@@ -1,130 +1,125 @@
// Inspired by react-hot-toast library
import * as React from "react"
import type {
ToastActionElement,
ToastProps,
} from "@/components/ui/toast"
import type { ToastActionElement, ToastProps } from "@/components/ui/toast"
const TOAST_LIMIT = 1
const TOAST_REMOVE_DELAY = 1000000
type ToasterToast = ToastProps & {
id: string
title?: React.ReactNode
description?: React.ReactNode
action?: ToastActionElement
id: string
title?: React.ReactNode
description?: React.ReactNode
action?: ToastActionElement
}
const actionTypes = {
ADD_TOAST: "ADD_TOAST",
UPDATE_TOAST: "UPDATE_TOAST",
DISMISS_TOAST: "DISMISS_TOAST",
REMOVE_TOAST: "REMOVE_TOAST",
ADD_TOAST: "ADD_TOAST",
UPDATE_TOAST: "UPDATE_TOAST",
DISMISS_TOAST: "DISMISS_TOAST",
REMOVE_TOAST: "REMOVE_TOAST",
} as const
let count = 0
function genId() {
count = (count + 1) % Number.MAX_SAFE_INTEGER
return count.toString()
count = (count + 1) % Number.MAX_SAFE_INTEGER
return count.toString()
}
type ActionType = typeof actionTypes
type Action =
| {
type: ActionType["ADD_TOAST"]
toast: ToasterToast
}
| {
type: ActionType["UPDATE_TOAST"]
toast: Partial<ToasterToast>
}
| {
type: ActionType["DISMISS_TOAST"]
toastId?: ToasterToast["id"]
}
| {
type: ActionType["REMOVE_TOAST"]
toastId?: ToasterToast["id"]
}
| {
type: ActionType["ADD_TOAST"]
toast: ToasterToast
}
| {
type: ActionType["UPDATE_TOAST"]
toast: Partial<ToasterToast>
}
| {
type: ActionType["DISMISS_TOAST"]
toastId?: ToasterToast["id"]
}
| {
type: ActionType["REMOVE_TOAST"]
toastId?: ToasterToast["id"]
}
interface State {
toasts: ToasterToast[]
toasts: ToasterToast[]
}
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
const addToRemoveQueue = (toastId: string) => {
if (toastTimeouts.has(toastId)) {
return
}
if (toastTimeouts.has(toastId)) {
return
}
const timeout = setTimeout(() => {
toastTimeouts.delete(toastId)
dispatch({
type: "REMOVE_TOAST",
toastId: toastId,
})
}, TOAST_REMOVE_DELAY)
const timeout = setTimeout(() => {
toastTimeouts.delete(toastId)
dispatch({
type: "REMOVE_TOAST",
toastId: toastId,
})
}, TOAST_REMOVE_DELAY)
toastTimeouts.set(toastId, timeout)
toastTimeouts.set(toastId, timeout)
}
export const reducer = (state: State, action: Action): State => {
switch (action.type) {
case "ADD_TOAST":
return {
...state,
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
}
switch (action.type) {
case "ADD_TOAST":
return {
...state,
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
}
case "UPDATE_TOAST":
return {
...state,
toasts: state.toasts.map((t) =>
t.id === action.toast.id ? { ...t, ...action.toast } : t
),
}
case "UPDATE_TOAST":
return {
...state,
toasts: state.toasts.map((t) => (t.id === action.toast.id ? { ...t, ...action.toast } : t)),
}
case "DISMISS_TOAST": {
const { toastId } = action
case "DISMISS_TOAST": {
const { toastId } = action
// ! Side effects ! - This could be extracted into a dismissToast() action,
// but I'll keep it here for simplicity
if (toastId) {
addToRemoveQueue(toastId)
} else {
state.toasts.forEach((toast) => {
addToRemoveQueue(toast.id)
})
}
// ! Side effects ! - This could be extracted into a dismissToast() action,
// but I'll keep it here for simplicity
if (toastId) {
addToRemoveQueue(toastId)
} else {
state.toasts.forEach((toast) => {
addToRemoveQueue(toast.id)
})
}
return {
...state,
toasts: state.toasts.map((t) =>
t.id === toastId || toastId === undefined
? {
...t,
open: false,
}
: t
),
}
}
case "REMOVE_TOAST":
if (action.toastId === undefined) {
return {
...state,
toasts: [],
}
}
return {
...state,
toasts: state.toasts.filter((t) => t.id !== action.toastId),
}
}
return {
...state,
toasts: state.toasts.map((t) =>
t.id === toastId || toastId === undefined
? {
...t,
open: false,
}
: t
),
}
}
case "REMOVE_TOAST":
if (action.toastId === undefined) {
return {
...state,
toasts: [],
}
}
return {
...state,
toasts: state.toasts.filter((t) => t.id !== action.toastId),
}
}
}
const listeners: Array<(state: State) => void> = []
@@ -132,61 +127,61 @@ const listeners: Array<(state: State) => void> = []
let memoryState: State = { toasts: [] }
function dispatch(action: Action) {
memoryState = reducer(memoryState, action)
listeners.forEach((listener) => {
listener(memoryState)
})
memoryState = reducer(memoryState, action)
listeners.forEach((listener) => {
listener(memoryState)
})
}
type Toast = Omit<ToasterToast, "id">
function toast({ ...props }: Toast) {
const id = genId()
const id = genId()
const update = (props: ToasterToast) =>
dispatch({
type: "UPDATE_TOAST",
toast: { ...props, id },
})
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
const update = (props: ToasterToast) =>
dispatch({
type: "UPDATE_TOAST",
toast: { ...props, id },
})
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
dispatch({
type: "ADD_TOAST",
toast: {
...props,
id,
open: true,
onOpenChange: (open) => {
if (!open) dismiss()
},
},
})
dispatch({
type: "ADD_TOAST",
toast: {
...props,
id,
open: true,
onOpenChange: (open) => {
if (!open) dismiss()
},
},
})
return {
id: id,
dismiss,
update,
}
return {
id: id,
dismiss,
update,
}
}
function useToast() {
const [state, setState] = React.useState<State>(memoryState)
const [state, setState] = React.useState<State>(memoryState)
React.useEffect(() => {
listeners.push(setState)
return () => {
const index = listeners.indexOf(setState)
if (index > -1) {
listeners.splice(index, 1)
}
}
}, [state])
React.useEffect(() => {
listeners.push(setState)
return () => {
const index = listeners.indexOf(setState)
if (index > -1) {
listeners.splice(index, 1)
}
}
}, [state])
return {
...state,
toast,
dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
}
return {
...state,
toast,
dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
}
}
export { useToast, toast }

View File

@@ -48,7 +48,7 @@
--muted-foreground: 240 5.03% 64.9%;
--accent: 240 3.7% 15.88%;
--accent-foreground: 0 0% 98.04%;
--destructive: 0 56.48% 42.35%;
--destructive: 0 59% 46%;
--destructive-foreground: 0 0% 98.04%;
--border: 240 2.86% 12%;
--input: 240 3.7% 15.88%;
@@ -68,7 +68,7 @@
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: url('/static/InterVariable.woff2?v=4.0') format('woff2');
src: url("/static/InterVariable.woff2?v=4.0") format("woff2");
}
@layer base {

View File

@@ -0,0 +1,62 @@
import { $direction } from "./stores"
import { i18n } from "@lingui/core"
import type { Messages } from "@lingui/core"
import languages from "@/lib/languages"
import { detect, fromUrl, fromStorage, fromNavigator } from "@lingui/detect-locale"
import { messages as enMessages } from "../locales/en/en.ts"
// let locale = detect(fromUrl("lang"), fromStorage("lang"), fromNavigator(), "en")
let locale = detect(fromStorage("lang"), fromNavigator(), "en")
// log if dev
if (import.meta.env.DEV) {
console.log("detected locale", locale)
}
// activates locale
function activateLocale(locale: string, messages: Messages = enMessages) {
i18n.load(locale, messages)
i18n.activate(locale)
document.documentElement.lang = locale
localStorage.setItem("lang", locale)
$direction.set(locale.startsWith("ar") ? "rtl" : "ltr")
}
// dynamically loads translations for the given locale
export async function dynamicActivate(locale: string) {
if (locale == "en") {
activateLocale(locale)
} else {
try {
const { messages }: { messages: Messages } = await import(`../locales/${locale}/${locale}.ts`)
activateLocale(locale, messages)
} catch (error) {
console.error(`Error loading ${locale}`, error)
activateLocale("en")
}
}
}
// handle zh variants
if (locale?.startsWith("zh-")) {
// map zh variants to zh-CN
const zhVariantMap: Record<string, string> = {
"zh-CN": "zh-CN",
"zh-SG": "zh-CN",
"zh-MY": "zh-CN",
zh: "zh-CN",
"zh-Hans": "zh-CN",
"zh-HK": "zh-HK",
"zh-TW": "zh-HK",
"zh-MO": "zh-HK",
"zh-Hant": "zh-HK",
}
dynamicActivate(zhVariantMap[locale] || "zh-CN")
} else {
locale = (locale || "en").split("-")[0]
// use en if locale is not in languages
if (!languages.some((l) => l.lang === locale)) {
locale = "en"
}
dynamicActivate(locale)
}

View File

@@ -0,0 +1,77 @@
export default [
{
lang: "ar",
label: "العربية",
e: "🇵🇸",
},
{
lang: "de",
label: "Deutsch",
e: "🇩🇪",
},
{
lang: "en",
label: "English",
e: "🇺🇸",
},
{
lang: "es",
label: "Español",
e: "🇲🇽",
},
{
lang: "fr",
label: "Français",
e: "🇫🇷",
},
{
lang: "it",
label: "Italiano",
e: "🇮🇹",
},
{
lang: "ja",
label: "日本語",
e: "🇯🇵",
},
{
lang: "ko",
label: "한국어",
e: "🇰🇷",
},
{
lang: "pt",
label: "Português",
e: "🇧🇷",
},
{
lang: "tr",
label: "Türkçe",
e: "🇹🇷",
},
{
lang: "ru",
label: "Русский",
e: "🇷🇺",
},
{
lang: "uk",
label: "Українська",
e: "🇺🇦",
},
{
lang: "vi",
label: "Tiếng Việt",
e: "🇻🇳",
},
{
lang: "zh-CN",
label: "简体中文",
e: "🇨🇳",
},
{
lang: "zh-HK",
label: "繁體中文",
e: "🇭🇰",
},
] as const

View File

@@ -1,9 +1,9 @@
import PocketBase from 'pocketbase'
import { atom, map, WritableAtom } from 'nanostores'
import { AlertRecord, ChartTimes, SystemRecord, UserSettings } from '@/types'
import PocketBase from "pocketbase"
import { atom, map, WritableAtom } from "nanostores"
import { AlertRecord, ChartTimes, SystemRecord, UserSettings } from "@/types"
/** PocketBase JS Client */
export const pb = new PocketBase('/')
export const pb = new PocketBase("/")
/** Store if user is authenticated */
export const $authenticated = atom(pb.authStore.isValid)
@@ -15,18 +15,18 @@ export const $systems = atom([] as SystemRecord[])
export const $alerts = atom([] as AlertRecord[])
/** SSH public key */
export const $publicKey = atom('')
export const $publicKey = atom("")
/** Beszel hub version */
export const $hubVersion = atom('')
export const $hubVersion = atom("")
/** Chart time period */
export const $chartTime = atom('1h') as WritableAtom<ChartTimes>
export const $chartTime = atom("1h") as WritableAtom<ChartTimes>
/** User settings */
export const $userSettings = map<UserSettings>({
chartTime: '1h',
emails: [pb.authStore.model?.email || ''],
chartTime: "1h",
emails: [pb.authStore.model?.email || ""],
})
// update local storage on change
$userSettings.subscribe((value) => {
@@ -35,7 +35,10 @@ $userSettings.subscribe((value) => {
})
/** Container chart filter */
export const $containerFilter = atom('')
export const $containerFilter = atom("")
/** Fallback copy to clipboard dialog content */
export const $copyContent = atom('')
export const $copyContent = atom("")
/** Direction for localization */
export const $direction = atom<"ltr" | "rtl">("ltr")

View File

@@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from 'react'
import { useEffect, useRef, useState } from "react"
// adapted from usehooks-ts/use-intersection-observer
@@ -72,7 +72,7 @@ type IntersectionReturn = {
export function useIntersectionObserver({
threshold = 0,
root = null,
rootMargin = '0%',
rootMargin = "0%",
freeze = true,
initialIsIntersecting = false,
onChange,
@@ -84,7 +84,7 @@ export function useIntersectionObserver({
entry: undefined,
}))
const callbackRef = useRef<UseIntersectionObserverOptions['onChange']>()
const callbackRef = useRef<UseIntersectionObserverOptions["onChange"]>()
callbackRef.current = onChange
@@ -95,7 +95,7 @@ export function useIntersectionObserver({
if (!ref) return
// Ensure the browser supports the Intersection Observer API
if (!('IntersectionObserver' in window)) return
if (!("IntersectionObserver" in window)) return
// Skip if frozen
if (frozen) return
@@ -104,14 +104,11 @@ export function useIntersectionObserver({
const observer = new IntersectionObserver(
(entries: IntersectionObserverEntry[]): void => {
const thresholds = Array.isArray(observer.thresholds)
? observer.thresholds
: [observer.thresholds]
const thresholds = Array.isArray(observer.thresholds) ? observer.thresholds : [observer.thresholds]
entries.forEach((entry) => {
const isIntersecting =
entry.isIntersecting &&
thresholds.some((threshold) => entry.intersectionRatio >= threshold)
entry.isIntersecting && thresholds.some((threshold) => entry.intersectionRatio >= threshold)
setState({ isIntersecting, entry })
@@ -149,13 +146,7 @@ export function useIntersectionObserver({
const prevRef = useRef<Element | null>(null)
useEffect(() => {
if (
!ref &&
state.entry?.target &&
!freeze &&
!frozen &&
prevRef.current !== state.entry.target
) {
if (!ref && state.entry?.target && !freeze && !frozen && prevRef.current !== state.entry.target) {
prevRef.current = state.entry.target
setState({ isIntersecting: initialIsIntersecting, entry: undefined })
}

View File

@@ -1,14 +1,15 @@
import { toast } from '@/components/ui/use-toast'
import { type ClassValue, clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'
import { $alerts, $copyContent, $systems, $userSettings, pb } from './stores'
import { AlertRecord, ChartTimeData, ChartTimes, SystemRecord } from '@/types'
import { RecordModel, RecordSubscription } from 'pocketbase'
import { WritableAtom } from 'nanostores'
import { timeDay, timeHour } from 'd3-time'
import { useEffect, useState } from 'react'
import { CpuIcon, HardDriveIcon, MemoryStickIcon, ServerIcon } from 'lucide-react'
import { EthernetIcon, ThermometerIcon } from '@/components/ui/icons'
import { toast } from "@/components/ui/use-toast"
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"
import { $alerts, $copyContent, $systems, $userSettings, pb } from "./stores"
import { AlertInfo, AlertRecord, ChartTimeData, ChartTimes, SystemRecord } from "@/types"
import { RecordModel, RecordSubscription } from "pocketbase"
import { WritableAtom } from "nanostores"
import { timeDay, timeHour } from "d3-time"
import { useEffect, useState } from "react"
import { CpuIcon, HardDriveIcon, MemoryStickIcon, ServerIcon } from "lucide-react"
import { EthernetIcon, ThermometerIcon } from "@/components/ui/icons"
import { t } from "@lingui/macro"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
@@ -21,7 +22,7 @@ export async function copyToClipboard(content: string) {
await navigator.clipboard.writeText(content)
toast({
duration,
description: 'Copied to clipboard',
description: t`Copied to clipboard`,
})
} catch (e: any) {
$copyContent.set(content)
@@ -29,22 +30,22 @@ export async function copyToClipboard(content: string) {
}
const verifyAuth = () => {
pb.collection('users')
pb.collection("users")
.authRefresh()
.catch(() => {
pb.authStore.clear()
toast({
title: 'Failed to authenticate',
description: 'Please log in again',
variant: 'destructive',
title: t`Failed to authenticate`,
description: t`Please log in again`,
variant: "destructive",
})
})
}
export const updateSystemList = async () => {
const records = await pb
.collection<SystemRecord>('systems')
.getFullList({ sort: '+name', fields: 'id,name,host,info,status' })
.collection<SystemRecord>("systems")
.getFullList({ sort: "+name", fields: "id,name,host,info,status" })
if (records.length) {
$systems.set(records)
} else {
@@ -53,52 +54,36 @@ export const updateSystemList = async () => {
}
export const updateAlerts = () => {
pb.collection('alerts')
.getFullList<AlertRecord>({ fields: 'id,name,system,value,min,triggered', sort: 'updated' })
pb.collection("alerts")
.getFullList<AlertRecord>({ fields: "id,name,system,value,min,triggered", sort: "updated" })
.then((records) => {
$alerts.set(records)
})
}
const hourWithMinutesFormatter = new Intl.DateTimeFormat(undefined, {
hour: 'numeric',
minute: 'numeric',
hour: "numeric",
minute: "numeric",
})
export const hourWithMinutes = (timestamp: string) => {
return hourWithMinutesFormatter.format(new Date(timestamp))
}
const shortDateFormatter = new Intl.DateTimeFormat(undefined, {
day: 'numeric',
month: 'short',
hour: 'numeric',
minute: 'numeric',
day: "numeric",
month: "short",
hour: "numeric",
minute: "numeric",
})
export const formatShortDate = (timestamp: string) => {
// console.log('ts', timestamp)
return shortDateFormatter.format(new Date(timestamp))
}
// const dayTimeFormatter = new Intl.DateTimeFormat(undefined, {
// // day: 'numeric',
// // month: 'short',
// hour: 'numeric',
// weekday: 'short',
// minute: 'numeric',
// // dateStyle: 'short',
// })
// export const formatDayTime = (timestamp: string) => {
// // console.log('ts', timestamp)
// return dayTimeFormatter.format(new Date(timestamp))
// }
const dayFormatter = new Intl.DateTimeFormat(undefined, {
day: 'numeric',
month: 'short',
// dateStyle: 'medium',
day: "numeric",
month: "short",
})
export const formatDay = (timestamp: string) => {
// console.log('ts', timestamp)
return dayFormatter.format(new Date(timestamp))
}
@@ -106,19 +91,14 @@ export const updateFavicon = (newIcon: string) => {
;(document.querySelector("link[rel='icon']") as HTMLLinkElement).href = `/static/${newIcon}`
}
export const isAdmin = () => pb.authStore.model?.role === 'admin'
export const isReadOnlyUser = () => pb.authStore.model?.role === 'readonly'
// export const isDefaultUser = () => pb.authStore.model?.role === 'user'
export const isAdmin = () => pb.authStore.model?.role === "admin"
export const isReadOnlyUser = () => pb.authStore.model?.role === "readonly"
/** Update systems / alerts list when records change */
export function updateRecordList<T extends RecordModel>(
e: RecordSubscription<T>,
$store: WritableAtom<T[]>
) {
export function updateRecordList<T extends RecordModel>(e: RecordSubscription<T>, $store: WritableAtom<T[]>) {
const curRecords = $store.get()
const newRecords = []
// console.log('e', e)
if (e.action === 'delete') {
if (e.action === "delete") {
for (const server of curRecords) {
if (server.id !== e.record.id) {
newRecords.push(server)
@@ -143,51 +123,51 @@ export function updateRecordList<T extends RecordModel>(
export function getPbTimestamp(timeString: ChartTimes, d?: Date) {
d ||= chartTimeData[timeString].getOffset(new Date())
const year = d.getUTCFullYear()
const month = String(d.getUTCMonth() + 1).padStart(2, '0')
const day = String(d.getUTCDate()).padStart(2, '0')
const hours = String(d.getUTCHours()).padStart(2, '0')
const minutes = String(d.getUTCMinutes()).padStart(2, '0')
const seconds = String(d.getUTCSeconds()).padStart(2, '0')
const month = String(d.getUTCMonth() + 1).padStart(2, "0")
const day = String(d.getUTCDate()).padStart(2, "0")
const hours = String(d.getUTCHours()).padStart(2, "0")
const minutes = String(d.getUTCMinutes()).padStart(2, "0")
const seconds = String(d.getUTCSeconds()).padStart(2, "0")
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
}
export const chartTimeData: ChartTimeData = {
'1h': {
type: '1m',
"1h": {
type: "1m",
expectedInterval: 60_000,
label: '1 hour',
label: () => t`1 hour`,
// ticks: 12,
format: (timestamp: string) => hourWithMinutes(timestamp),
getOffset: (endTime: Date) => timeHour.offset(endTime, -1),
},
'12h': {
type: '10m',
"12h": {
type: "10m",
expectedInterval: 60_000 * 10,
label: '12 hours',
label: () => t`12 hours`,
ticks: 12,
format: (timestamp: string) => hourWithMinutes(timestamp),
getOffset: (endTime: Date) => timeHour.offset(endTime, -12),
},
'24h': {
type: '20m',
"24h": {
type: "20m",
expectedInterval: 60_000 * 20,
label: '24 hours',
label: () => t`24 hours`,
format: (timestamp: string) => hourWithMinutes(timestamp),
getOffset: (endTime: Date) => timeHour.offset(endTime, -24),
},
'1w': {
type: '120m',
"1w": {
type: "120m",
expectedInterval: 60_000 * 120,
label: '1 week',
label: () => t`1 week`,
ticks: 7,
format: (timestamp: string) => formatDay(timestamp),
getOffset: (endTime: Date) => timeDay.offset(endTime, -7),
},
'30d': {
type: '480m',
"30d": {
type: "480m",
expectedInterval: 60_000 * 480,
label: '30 days',
label: () => t`30 days`,
ticks: 30,
format: (timestamp: string) => formatDay(timestamp),
getOffset: (endTime: Date) => timeDay.offset(endTime, -30),
@@ -202,8 +182,8 @@ export function useYAxisWidth() {
function updateYAxisWidth(str: string) {
if (str.length > maxChars) {
maxChars = str.length
const div = document.createElement('div')
div.className = 'text-xs tabular-nums tracking-tighter table sr-only'
const div = document.createElement("div")
div.className = "text-xs tabular-nums tracking-tighter table sr-only"
div.innerHTML = str
clearTimeout(timeout)
timeout = setTimeout(() => {
@@ -249,7 +229,7 @@ function getStorageValue(key: string, defaultValue: any) {
}
/** Hook to sync value in local storage */
export const useLocalStorage = (key: string, defaultValue: any) => {
export function useLocalStorage<T>(key: string, defaultValue: T) {
key = `besz-${key}`
const [value, setValue] = useState(() => {
return getStorageValue(key, defaultValue)
@@ -263,20 +243,18 @@ export const useLocalStorage = (key: string, defaultValue: any) => {
export async function updateUserSettings() {
try {
const req = await pb.collection('user_settings').getFirstListItem('', { fields: 'settings' })
const req = await pb.collection("user_settings").getFirstListItem("", { fields: "settings" })
$userSettings.set(req.settings)
return
} catch (e) {
console.log('get settings', e)
console.log("get settings", e)
}
// create user settings if error fetching existing
try {
const createdSettings = await pb
.collection('user_settings')
.create({ user: pb.authStore.model!.id })
const createdSettings = await pb.collection("user_settings").create({ user: pb.authStore.model!.id })
$userSettings.set(createdSettings.settings)
} catch (e) {
console.log('create settings', e)
console.log("create settings", e)
}
}
@@ -290,51 +268,52 @@ export const getSizeAndUnit = (n: number, isGigabytes = true) => {
const sizeInGB = isGigabytes ? n : n / 1_000
if (sizeInGB >= 1_000) {
return { v: sizeInGB / 1_000, u: ' TB' }
return { v: sizeInGB / 1_000, u: " TB" }
} else if (sizeInGB >= 1) {
return { v: sizeInGB, u: ' GB' }
return { v: sizeInGB, u: " GB" }
}
return { v: n, u: ' MB' }
return { v: n, u: " MB" }
}
export const chartMargin = { top: 12 }
export const alertInfo = {
export const alertInfo: Record<string, AlertInfo> = {
Status: {
name: 'Status',
unit: '',
name: () => t`Status`,
unit: "",
icon: ServerIcon,
desc: 'Triggers when status switches between up and down.',
desc: () => t`Triggers when status switches between up and down`,
single: true,
},
CPU: {
name: 'CPU usage',
unit: '%',
name: () => t`CPU Usage`,
unit: "%",
icon: CpuIcon,
desc: 'Triggers when CPU usage exceeds a threshold.',
desc: () => t`Triggers when CPU usage exceeds a threshold`,
},
Memory: {
name: 'memory usage',
unit: '%',
name: () => t`Memory Usage`,
unit: "%",
icon: MemoryStickIcon,
desc: 'Triggers when memory usage exceeds a threshold.',
desc: () => t`Triggers when memory usage exceeds a threshold`,
},
Disk: {
name: 'disk usage',
unit: '%',
name: () => t`Disk Usage`,
unit: "%",
icon: HardDriveIcon,
desc: 'Triggers when usage of any disk exceeds a threshold.',
desc: () => t`Triggers when usage of any disk exceeds a threshold`,
},
Bandwidth: {
name: 'bandwidth',
unit: ' MB/s',
name: () => t`Bandwidth`,
unit: " MB/s",
icon: EthernetIcon,
desc: 'Triggers when combined up/down exceeds a threshold.',
desc: () => t`Triggers when combined up/down exceeds a threshold`,
max: 125,
},
Temperature: {
name: 'temperature',
unit: '°C',
name: () => t`Temperature`,
unit: "°C",
icon: ThermometerIcon,
desc: 'Triggers when any sensor exceeds a threshold.',
desc: () => t`Triggers when any sensor exceeds a threshold`,
},
}

View File

@@ -0,0 +1,808 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2024-11-01 11:30-0400\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: ar\n"
"Project-Id-Version: beszel\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-11-04 20:46\n"
"Last-Translator: \n"
"Language-Team: Arabic\n"
"Plural-Forms: nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5);\n"
"X-Crowdin-Project: beszel\n"
"X-Crowdin-Project-ID: 733311\n"
"X-Crowdin-Language: ar\n"
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 16\n"
#: src/components/routes/system.tsx:242
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# يوم} other {# أيام}}"
#: src/components/routes/system.tsx:240
msgid "{hours, plural, one {# hour} other {# hours}}"
msgstr "{hours, plural, one {# ساعة} other {# ساعات}}"
#: src/lib/utils.ts:139
msgid "1 hour"
msgstr "1 ساعة"
#: src/lib/utils.ts:162
msgid "1 week"
msgstr "1 أسبوع"
#: src/lib/utils.ts:147
msgid "12 hours"
msgstr "12 ساعة"
#: src/lib/utils.ts:155
msgid "24 hours"
msgstr "24 ساعة"
#: src/lib/utils.ts:170
msgid "30 days"
msgstr "30 يومًا"
#. Table column
#: src/components/systems-table/systems-table.tsx:207
msgid "Actions"
msgstr "إجراءات"
#: src/components/routes/home.tsx:62
msgid "Active Alerts"
msgstr "التنبيهات النشطة"
#: src/components/add-system.tsx:74
msgid "Add <0>System</0>"
msgstr "إضافة <0>نظام</0>"
#: src/components/add-system.tsx:83
msgid "Add New System"
msgstr "إضافة نظام جديد"
#: src/components/add-system.tsx:167
#: src/components/add-system.tsx:178
msgid "Add system"
msgstr "إضافة نظام"
#: src/components/routes/settings/notifications.tsx:156
msgid "Add URL"
msgstr "إضافة عنوان URL"
#: src/components/routes/settings/general.tsx:81
msgid "Adjust display options for charts."
msgstr "تعديل خيارات العرض للرسوم البيانية."
#: src/components/command-palette.tsx:133
#: src/components/command-palette.tsx:146
#: src/components/command-palette.tsx:160
#: src/components/command-palette.tsx:174
#: src/components/command-palette.tsx:189
#: src/components/command-palette.tsx:204
msgid "Admin"
msgstr "مسؤول"
#: src/components/systems-table/systems-table.tsx:186
msgid "Agent"
msgstr "وكيل"
#: src/components/alerts/alert-button.tsx:32
#: src/components/alerts/alert-button.tsx:68
msgid "Alerts"
msgstr "التنبيهات"
#: src/components/alerts/alert-button.tsx:88
#: src/components/systems-table/systems-table.tsx:317
msgid "All Systems"
msgstr "جميع الأنظمة"
#: src/components/systems-table/systems-table.tsx:261
msgid "Are you sure you want to delete {name}?"
msgstr "هل أنت متأكد أنك تريد حذف {name}؟"
#: src/components/command-palette.tsx:186
#: src/components/navbar.tsx:102
msgid "Auth Providers"
msgstr "مزودو المصادقة"
#: src/components/copy-to-clipboard.tsx:16
msgid "Automatic copy requires a secure context."
msgstr "النسخ التلقائي يتطلب سياقًا آمنًا."
#: src/components/routes/system.tsx:568
msgid "Average"
msgstr "متوسط"
#: src/components/routes/system.tsx:387
msgid "Average CPU utilization of containers"
msgstr "متوسط استخدام وحدة المعالجة المركزية للحاويات"
#: src/components/alerts/alerts-system.tsx:204
msgid "Average exceeds <0>{value}{0}</0>"
msgstr "المتوسط يتجاوز <0>{value}{0}</0>"
#: src/components/routes/system.tsx:376
msgid "Average system-wide CPU utilization"
msgstr "متوسط استخدام وحدة المعالجة المركزية على مستوى النظام"
#: src/components/command-palette.tsx:171
#: src/components/navbar.tsx:94
msgid "Backups"
msgstr "النسخ الاحتياطية"
#: src/components/routes/system.tsx:436
#: src/lib/utils.ts:307
msgid "Bandwidth"
msgstr "عرض النطاق الترددي"
#: src/components/login/auth-form.tsx:313
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
msgstr "يدعم Beszel OpenID Connect والعديد من مزودي المصادقة OAuth2."
#: src/components/routes/settings/notifications.tsx:127
msgid "Beszel uses <0>Shoutrrr</0> to integrate with popular notification services."
msgstr "يستخدم Beszel <0>Shoutrrr</0> للتكامل مع خدمات الإشعارات الشهيرة."
#: src/components/add-system.tsx:88
msgid "Binary"
msgstr "ثنائي"
#: src/components/charts/mem-chart.tsx:89
msgid "Cache / Buffers"
msgstr "ذاكرة التخزين المؤقت / المخازن المؤقتة"
#: src/components/systems-table/systems-table.tsx:272
msgid "Cancel"
msgstr "إلغاء"
#: src/components/routes/settings/config-yaml.tsx:68
msgid "Caution - potential data loss"
msgstr "تحذير - فقدان محتمل للبيانات"
#: src/components/routes/settings/general.tsx:36
msgid "Change general application options."
msgstr "تغيير خيارات التطبيق العامة."
#: src/components/routes/settings/general.tsx:78
msgid "Chart options"
msgstr "خيارات الرسم البياني"
#: src/components/login/forgot-pass-form.tsx:34
msgid "Check {email} for a reset link."
msgstr "تحقق من {email} للحصول على رابط إعادة التعيين."
#: src/components/routes/settings/layout.tsx:40
msgid "Check logs for more details."
msgstr "تحقق من السجلات لمزيد من التفاصيل."
#: src/components/routes/settings/notifications.tsx:183
msgid "Check your notification service"
msgstr "تحقق من خدمة الإشعارات الخاصة بك"
#: src/components/add-system.tsx:153
msgid "Click to copy"
msgstr "انقر للنسخ"
#. Context: table columns
#: src/components/systems-table/systems-table.tsx:328
msgid "Columns"
msgstr "أعمدة"
#: src/components/login/forgot-pass-form.tsx:83
#: src/components/login/forgot-pass-form.tsx:89
msgid "Command line instructions"
msgstr "تعليمات سطر الأوامر"
#: src/components/routes/settings/notifications.tsx:77
msgid "Configure how you receive alert notifications."
msgstr "قم بتكوين كيفية تلقي إشعارات التنبيه."
#: src/components/login/auth-form.tsx:189
#: src/components/login/auth-form.tsx:194
msgid "Confirm password"
msgstr "تأكيد كلمة المرور"
#: src/components/systems-table/systems-table.tsx:278
msgid "Continue"
msgstr "متابعة"
#: src/lib/utils.ts:25
msgid "Copied to clipboard"
msgstr "تم النسخ إلى الحافظة"
#: src/components/add-system.tsx:164
msgid "Copy"
msgstr "نسخ"
#: src/components/systems-table/systems-table.tsx:247
msgid "Copy host"
msgstr "نسخ المضيف"
#: src/components/add-system.tsx:175
msgid "Copy Linux command"
msgstr "نسخ أمر لينكس"
#: src/components/copy-to-clipboard.tsx:13
msgid "Copy text"
msgstr "نسخ النص"
#: src/components/systems-table/systems-table.tsx:152
msgid "CPU"
msgstr "المعالج"
#: src/components/charts/area-chart.tsx:52
#: src/components/routes/system.tsx:375
#: src/lib/utils.ts:289
msgid "CPU Usage"
msgstr "استخدام وحدة المعالجة المركزية"
#: src/components/login/auth-form.tsx:215
msgid "Create account"
msgstr "إنشاء حساب"
#. Dark theme
#: src/components/mode-toggle.tsx:21
msgid "Dark"
msgstr "داكن"
#: src/components/command-palette.tsx:82
#: src/components/routes/home.tsx:35
msgid "Dashboard"
msgstr "لوحة التحكم"
#: src/components/routes/settings/general.tsx:85
msgid "Default time period"
msgstr "الفترة الزمنية الافتراضية"
#: src/components/systems-table/systems-table.tsx:253
msgid "Delete"
msgstr "حذف"
#: src/components/systems-table/systems-table.tsx:166
msgid "Disk"
msgstr "القرص"
#: src/components/routes/system.tsx:426
msgid "Disk I/O"
msgstr "إدخال/إخراج القرص"
#: src/components/charts/disk-chart.tsx:74
#: src/components/routes/system.tsx:415
#: src/lib/utils.ts:301
msgid "Disk Usage"
msgstr "استخدام القرص"
#: src/components/routes/system.tsx:495
msgid "Disk usage of {extraFsName}"
msgstr "استخدام القرص لـ {extraFsName}"
#: src/components/routes/system.tsx:386
msgid "Docker CPU Usage"
msgstr "استخدام CPU لـ Docker"
#: src/components/routes/system.tsx:407
msgid "Docker Memory Usage"
msgstr "استخدام الذاكرة لـ Docker"
#: src/components/routes/system.tsx:452
msgid "Docker Network I/O"
msgstr "إدخال/إخراج الشبكة لـ Docker"
#: src/components/command-palette.tsx:125
msgid "Documentation"
msgstr "التوثيق"
#: src/components/login/auth-form.tsx:158
msgid "email"
msgstr "البريد الإلكتروني"
#: src/components/login/auth-form.tsx:152
#: src/components/login/forgot-pass-form.tsx:53
msgid "Email"
msgstr "البريد الإلكتروني"
#: src/components/routes/settings/notifications.tsx:91
msgid "Email notifications"
msgstr "إشعارات البريد الإلكتروني"
#: src/components/login/login.tsx:36
msgid "Enter email address to reset password"
msgstr "أدخل عنوان البريد الإلكتروني لإعادة تعيين كلمة المرور"
#: src/components/routes/settings/notifications.tsx:111
msgid "Enter email address..."
msgstr "أدخل عنوان البريد الإلكتروني..."
#: src/components/login/auth-form.tsx:256
#: src/components/routes/settings/config-yaml.tsx:28
#: src/components/routes/settings/notifications.tsx:187
msgid "Error"
msgstr "خطأ"
#: src/components/routes/home.tsx:81
msgid "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
msgstr "يتجاوز {0}{1} في آخر {2, plural, one {# دقيقة} other {# دقائق}}"
#: src/components/routes/settings/config-yaml.tsx:72
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
msgstr "سيتم حذف الأنظمة الحالية غير المعرفة في <0>config.yml</0>. يرجى عمل نسخ احتياطية بانتظام."
#: src/components/routes/settings/config-yaml.tsx:93
msgid "Export configuration"
msgstr "تصدير التكوين"
#: src/components/routes/settings/config-yaml.tsx:48
msgid "Export your current systems configuration."
msgstr "تصدير تكوين الأنظمة الحالية الخاصة بك."
#: src/lib/utils.ts:38
msgid "Failed to authenticate"
msgstr "فشل في المصادقة"
#: src/components/routes/settings/layout.tsx:39
#: src/components/routes/settings/notifications.tsx:62
msgid "Failed to save settings"
msgstr "فشل في حفظ الإعدادات"
#: src/components/routes/settings/notifications.tsx:188
msgid "Failed to send test notification"
msgstr "فشل في إرسال إشعار الاختبار"
#: src/components/alerts/alerts-system.tsx:27
msgid "Failed to update alert"
msgstr "فشل في تحديث التنبيه"
#: src/components/routes/system.tsx:539
#: src/components/systems-table/systems-table.tsx:324
msgid "Filter..."
msgstr "تصفية..."
#: src/components/alerts/alerts-system.tsx:225
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
msgstr "لمدة <0>{min}</0> {min, plural, one {دقيقة} other {دقائق}}"
#: src/components/login/auth-form.tsx:337
msgid "Forgot password?"
msgstr "هل نسيت كلمة المرور؟"
#. Context: General settings
#: src/components/routes/settings/general.tsx:33
#: src/components/routes/settings/layout.tsx:51
msgid "General"
msgstr "عام"
#: src/components/add-system.tsx:119
msgid "Host / IP"
msgstr "مضيف / IP"
#: src/components/login/forgot-pass-form.tsx:93
msgid "If you've lost the password to your admin account, you may reset it using the following command."
msgstr "إذا فقدت كلمة المرور لحساب المسؤول الخاص بك، يمكنك إعادة تعيينها باستخدام الأمر التالي."
#: src/components/login/auth-form.tsx:16
msgid "Invalid email address."
msgstr "عنوان البريد الإلكتروني غير صالح."
#. Linux kernel
#: src/components/routes/system.tsx:254
msgid "Kernel"
msgstr "كيرنل"
#: src/components/routes/settings/general.tsx:45
msgid "Language"
msgstr "اللغة"
#. Light theme
#: src/components/mode-toggle.tsx:16
msgid "Light"
msgstr "فاتح"
#: src/components/navbar.tsx:113
msgid "Log Out"
msgstr "تسجيل الخروج"
#: src/components/login/login.tsx:17
msgid "Login"
msgstr "تسجيل الدخول"
#: src/components/login/auth-form.tsx:42
#: src/components/login/forgot-pass-form.tsx:15
msgid "Login attempt failed"
msgstr "فشل محاولة تسجيل الدخول"
#: src/components/command-palette.tsx:157
#: src/components/navbar.tsx:86
msgid "Logs"
msgstr "السجلات"
#: src/components/routes/settings/notifications.tsx:80
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
msgstr "هل تبحث عن مكان لإنشاء التنبيهات؟ انقر على أيقونات الجرس <0/> في جدول الأنظمة."
#: src/components/routes/settings/layout.tsx:85
msgid "Manage display and notification preferences."
msgstr "إدارة تفضيلات العرض والإشعارات."
#. Chart select field. Please try to keep this short.
#: src/components/routes/system.tsx:571
msgid "Max 1 min"
msgstr "1 دقيقة كحد"
#: src/components/systems-table/systems-table.tsx:159
msgid "Memory"
msgstr "الذاكرة"
#: src/components/routes/system.tsx:397
#: src/lib/utils.ts:295
msgid "Memory Usage"
msgstr "استخدام الذاكرة"
#: src/components/routes/system.tsx:408
msgid "Memory usage of docker containers"
msgstr "استخدام الذاكرة لحاويات Docker"
#: src/components/add-system.tsx:113
msgid "Name"
msgstr "الاسم"
#: src/components/systems-table/systems-table.tsx:173
msgid "Net"
msgstr "الشبكة"
#: src/components/routes/system.tsx:453
msgid "Network traffic of docker containers"
msgstr "حركة مرور الشبكة لحاويات Docker"
#: src/components/routes/system.tsx:438
msgid "Network traffic of public interfaces"
msgstr "حركة مرور الشبكة للواجهات العامة"
#: src/components/command-palette.tsx:50
msgid "No results found."
msgstr "لم يتم العثور على نتائج."
#: src/components/systems-table/systems-table.tsx:400
msgid "No systems found."
msgstr "لم يتم العثور على أنظمة."
#: src/components/command-palette.tsx:111
#: src/components/routes/settings/layout.tsx:56
#: src/components/routes/settings/notifications.tsx:74
msgid "Notifications"
msgstr "الإشعارات"
#: src/components/login/auth-form.tsx:308
msgid "OAuth 2 / OIDC support"
msgstr "دعم OAuth 2 / OIDC"
#: src/components/routes/settings/config-yaml.tsx:61
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
msgstr "في كل إعادة تشغيل، سيتم تحديث الأنظمة في قاعدة البيانات لتتطابق مع الأنظمة المعرفة في الملف."
#: src/components/systems-table/systems-table.tsx:219
msgid "Open menu"
msgstr "فتح القائمة"
#: src/components/login/auth-form.tsx:227
msgid "Or continue with"
msgstr "أو المتابعة باستخدام"
#: src/components/alerts/alert-button.tsx:109
msgid "Overwrite existing alerts"
msgstr "الكتابة فوق التنبيهات الحالية"
#: src/components/command-palette.tsx:85
msgid "Page"
msgstr "صفحة"
#: src/components/command-palette.tsx:72
msgid "Pages / Settings"
msgstr "الصفحات / الإعدادات"
#: src/components/login/auth-form.tsx:171
#: src/components/login/auth-form.tsx:176
msgid "Password"
msgstr "كلمة المرور"
#: src/components/login/auth-form.tsx:17
msgid "Password must be at least 10 characters."
msgstr "يجب أن تكون كلمة المرور مكونة من 10 أحرف على الأقل."
#: src/components/login/forgot-pass-form.tsx:33
msgid "Password reset request received"
msgstr "تم استلام طلب إعادة تعيين كلمة المرور"
#: src/components/systems-table/systems-table.tsx:241
msgid "Pause"
msgstr "إيقاف مؤقت"
#: src/components/routes/settings/notifications.tsx:95
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
msgstr "يرجى <0>تكوين خادم SMTP</0> لضمان تسليم التنبيهات."
#: src/components/alerts/alerts-system.tsx:28
msgid "Please check logs for more details."
msgstr "يرجى التحقق من السجلات لمزيد من التفاصيل."
#: src/components/login/auth-form.tsx:43
#: src/components/login/forgot-pass-form.tsx:16
msgid "Please check your credentials and try again"
msgstr "يرجى التحقق من بيانات الاعتماد الخاصة بك والمحاولة مرة أخرى"
#: src/components/login/login.tsx:34
msgid "Please create an admin account"
msgstr "يرجى إنشاء حساب مسؤول"
#: src/components/login/auth-form.tsx:257
msgid "Please enable pop-ups for this site"
msgstr "يرجى تمكين النوافذ المنبثقة لهذا الموقع"
#: src/lib/utils.ts:39
msgid "Please log in again"
msgstr "يرجى تسجيل الدخول مرة أخرى"
#: src/components/login/auth-form.tsx:316
msgid "Please see <0>the documentation</0> for instructions."
msgstr "يرجى الاطلاع على <0>التوثيق</0> للحصول على التعليمات."
#: src/components/login/login.tsx:38
msgid "Please sign in to your account"
msgstr "يرجى تسجيل الدخول إلى حسابك"
#: src/components/add-system.tsx:125
msgid "Port"
msgstr "المنفذ"
#: src/components/routes/system.tsx:398
msgid "Precise utilization at the recorded time"
msgstr "الاستخدام الدقيق في الوقت المسجل"
#: src/components/routes/settings/general.tsx:58
msgid "Preferred Language"
msgstr "اللغة المفضلة"
#. Use 'Key' if your language requires many more characters
#: src/components/add-system.tsx:131
msgid "Public Key"
msgstr "المفتاح العام"
#. Context is disk read
#: src/components/charts/area-chart.tsx:56
#: src/components/charts/area-chart.tsx:66
msgid "Read"
msgstr "قراءة"
#. Context is network bytes received (download)
#: src/components/charts/area-chart.tsx:61
msgid "Received"
msgstr "تم الاستلام"
#: src/components/login/forgot-pass-form.tsx:76
msgid "Reset Password"
msgstr "إعادة تعيين كلمة المرور"
#: src/components/systems-table/systems-table.tsx:236
msgid "Resume"
msgstr "استئناف"
#: src/components/routes/settings/notifications.tsx:117
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
msgstr "احفظ العنوان باستخدام مفتاح الإدخال أو الفاصلة. اتركه فارغًا لتعطيل إشعارات البريد الإلكتروني."
#: src/components/routes/settings/general.tsx:106
#: src/components/routes/settings/notifications.tsx:167
msgid "Save Settings"
msgstr "حفظ الإعدادات"
#: src/components/navbar.tsx:142
msgid "Search"
msgstr "بحث"
#: src/components/command-palette.tsx:47
msgid "Search for systems or settings..."
msgstr "البحث عن الأنظمة أو الإعدادات..."
#: src/components/alerts/alert-button.tsx:71
msgid "See <0>notification settings</0> to configure how you receive alerts."
msgstr "راجع <0>إعدادات الإشعارات</0> لتكوين كيفية تلقي التنبيهات."
#. Context is network bytes sent (upload)
#: src/components/charts/area-chart.tsx:60
msgid "Sent"
msgstr "تم الإرسال"
#: src/components/routes/settings/general.tsx:100
msgid "Sets the default time range for charts when a system is viewed."
msgstr "يحدد النطاق الزمني الافتراضي للرسوم البيانية عند عرض النظام."
#: src/components/command-palette.tsx:96
#: src/components/command-palette.tsx:99
#: src/components/command-palette.tsx:114
#: src/components/routes/settings/layout.tsx:71
#: src/components/routes/settings/layout.tsx:82
msgid "Settings"
msgstr "الإعدادات"
#: src/components/routes/settings/layout.tsx:33
msgid "Settings saved"
msgstr "تم حفظ الإعدادات"
#: src/components/login/auth-form.tsx:215
msgid "Sign in"
msgstr "تسجيل الدخول"
#: src/components/command-palette.tsx:201
msgid "SMTP settings"
msgstr "إعدادات SMTP"
#: src/lib/utils.ts:282
msgid "Status"
msgstr "الحالة"
#: src/components/routes/system.tsx:467
msgid "Swap space used by the system"
msgstr "مساحة التبديل المستخدمة من قبل النظام"
#: src/components/routes/system.tsx:466
msgid "Swap Usage"
msgstr "استخدام التبديل"
#. System theme
#: src/components/mode-toggle.tsx:26
#: src/components/systems-table/systems-table.tsx:110
#: src/components/systems-table/systems-table.tsx:121
msgid "System"
msgstr "النظام"
#: src/components/navbar.tsx:78
msgid "Systems"
msgstr "الأنظمة"
#: src/components/routes/settings/config-yaml.tsx:55
msgid "Systems may be managed in a <0>config.yml</0> file inside your data directory."
msgstr "يمكن إدارة الأنظمة في ملف <0>config.yml</0> داخل دليل البيانات الخاص بك."
#: src/components/routes/system.tsx:477
#: src/lib/utils.ts:314
msgid "Temperature"
msgstr "درجة الحرارة"
#: src/components/routes/system.tsx:478
msgid "Temperatures of system sensors"
msgstr "درجات حرارة مستشعرات النظام"
#: src/components/routes/settings/notifications.tsx:211
msgid "Test <0>URL</0>"
msgstr "اختبار <0>URL</0>"
#: src/components/routes/settings/notifications.tsx:182
msgid "Test notification sent"
msgstr "تم إرسال إشعار الاختبار"
#: src/components/add-system.tsx:104
msgid "The agent must be running on the system to connect. Copy the installation command for the agent below."
msgstr "يجب أن يكون الوكيل قيد التشغيل على النظام للاتصال. انسخ أمر التثبيت للوكيل أدناه."
#: src/components/add-system.tsx:95
msgid "The agent must be running on the system to connect. Copy the<0>docker-compose.yml</0> for the agent below."
msgstr "يجب أن يكون الوكيل قيد التشغيل على النظام للاتصال. انسخ <0>docker-compose.yml</0> للوكيل أدناه."
#: src/components/login/forgot-pass-form.tsx:98
msgid "Then log into the backend and reset your user account password in the users table."
msgstr "ثم قم بتسجيل الدخول إلى الواجهة الخلفية وأعد تعيين كلمة مرور حساب المستخدم الخاص بك في جدول المستخدمين."
#: src/components/systems-table/systems-table.tsx:264
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
msgstr "لا يمكن التراجع عن هذا الإجراء. سيؤدي ذلك إلى حذف جميع السجلات الحالية لـ {name} من قاعدة البيانات بشكل دائم."
#: src/components/routes/system.tsx:507
msgid "Throughput of {extraFsName}"
msgstr "معدل نقل {extraFsName}"
#: src/components/routes/system.tsx:427
msgid "Throughput of root filesystem"
msgstr "معدل نقل نظام الملفات الجذر"
#: src/components/routes/settings/notifications.tsx:106
msgid "To email(s)"
msgstr "إلى البريد الإلكتروني"
#: src/components/routes/system.tsx:350
#: src/components/routes/system.tsx:363
msgid "Toggle grid"
msgstr "تبديل الشبكة"
#: src/components/mode-toggle.tsx:33
msgid "Toggle theme"
msgstr "تبديل السمة"
#: src/lib/utils.ts:317
msgid "Triggers when any sensor exceeds a threshold"
msgstr "يتم التفعيل عندما <20><>تجاوز أي مستشعر عتبة معينة"
#: src/lib/utils.ts:310
msgid "Triggers when combined up/down exceeds a threshold"
msgstr "يتم التفعيل عندما يتجاوز الجمع بين الصعود/الهبوط عتبة معينة"
#: src/lib/utils.ts:292
msgid "Triggers when CPU usage exceeds a threshold"
msgstr "يتم التفعيل عندما يتجاوز استخدام وحدة المعالجة المركزية عتبة معينة"
#: src/lib/utils.ts:298
msgid "Triggers when memory usage exceeds a threshold"
msgstr "يتم التفعيل عندما يتجاوز استخدام الذاكرة عتبة معينة"
#: src/lib/utils.ts:285
msgid "Triggers when status switches between up and down"
msgstr "يتم التفعيل عندما يتغير الحالة بين التشغيل والإيقاف"
#: src/lib/utils.ts:304
msgid "Triggers when usage of any disk exceeds a threshold"
msgstr "يتم التفعيل عندما يتجاوز استخدام أي قرص عتبة معينة"
#: src/components/systems-table/systems-table.tsx:320
msgid "Updated in real time. Click on a system to view information."
msgstr "محدث في الوقت الحقيقي. انقر على نظام لعرض المعلومات."
#: src/components/routes/system.tsx:253
msgid "Uptime"
msgstr "مدة التشغيل"
#: src/components/routes/system.tsx:494
msgid "Usage"
msgstr "الاستخدام"
#: src/components/routes/system.tsx:415
msgid "Usage of root partition"
msgstr "استخدام القسم الجذر"
#: src/components/charts/mem-chart.tsx:65
#: src/components/charts/swap-chart.tsx:56
msgid "Used"
msgstr "مستخدم"
#: src/components/login/auth-form.tsx:138
msgid "username"
msgstr "اسم المستخدم"
#: src/components/login/auth-form.tsx:131
msgid "Username"
msgstr "اسم المستخدم"
#: src/components/command-palette.tsx:143
#: src/components/navbar.tsx:70
msgid "Users"
msgstr "المستخدمون"
#: src/components/routes/system.tsx:603
msgid "Waiting for enough records to display"
msgstr "في انتظار وجود سجلات كافية للعرض"
#: src/components/routes/settings/general.tsx:48
msgid "Want to help us make our translations even better? Check out <0>Crowdin</0> for more details."
msgstr "هل تريد مساعدتنا في تحسين ترجماتنا؟ تحقق من <0>Crowdin</0> لمزيد من التفاصيل."
#: src/components/routes/settings/notifications.tsx:124
msgid "Webhook / Push notifications"
msgstr "إشعارات Webhook / Push"
#. Context is disk write
#: src/components/charts/area-chart.tsx:55
#: src/components/charts/area-chart.tsx:65
msgid "Write"
msgstr "كتابة"
#: src/components/routes/settings/layout.tsx:61
msgid "YAML Config"
msgstr "تكوين YAML"
#: src/components/routes/settings/config-yaml.tsx:45
msgid "YAML Configuration"
msgstr "تكوين YAML"
#: src/components/routes/settings/layout.tsx:34
msgid "Your user settings have been updated."
msgstr "تم تحديث إعدادات المستخدم الخاصة بك."

View File

@@ -0,0 +1,808 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2024-11-01 11:30-0400\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: de\n"
"Project-Id-Version: beszel\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-11-04 20:46\n"
"Last-Translator: \n"
"Language-Team: German\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: beszel\n"
"X-Crowdin-Project-ID: 733311\n"
"X-Crowdin-Language: de\n"
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 16\n"
#: src/components/routes/system.tsx:242
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# Tag} other {# Tage}}"
#: src/components/routes/system.tsx:240
msgid "{hours, plural, one {# hour} other {# hours}}"
msgstr "{hours, plural, one {# Stunde} other {# Stunden}}"
#: src/lib/utils.ts:139
msgid "1 hour"
msgstr "1 Stunde"
#: src/lib/utils.ts:162
msgid "1 week"
msgstr "1 Woche"
#: src/lib/utils.ts:147
msgid "12 hours"
msgstr "12 Stunden"
#: src/lib/utils.ts:155
msgid "24 hours"
msgstr "24 Stunden"
#: src/lib/utils.ts:170
msgid "30 days"
msgstr "30 Tage"
#. Table column
#: src/components/systems-table/systems-table.tsx:207
msgid "Actions"
msgstr "Aktionen"
#: src/components/routes/home.tsx:62
msgid "Active Alerts"
msgstr "Aktive Warnungen"
#: src/components/add-system.tsx:74
msgid "Add <0>System</0>"
msgstr "<0>System</0> hinzufügen"
#: src/components/add-system.tsx:83
msgid "Add New System"
msgstr "Neues System hinzufügen"
#: src/components/add-system.tsx:167
#: src/components/add-system.tsx:178
msgid "Add system"
msgstr "System hinzufügen"
#: src/components/routes/settings/notifications.tsx:156
msgid "Add URL"
msgstr "URL hinzufügen"
#: src/components/routes/settings/general.tsx:81
msgid "Adjust display options for charts."
msgstr "Anzeigeoptionen für Diagramme anpassen."
#: src/components/command-palette.tsx:133
#: src/components/command-palette.tsx:146
#: src/components/command-palette.tsx:160
#: src/components/command-palette.tsx:174
#: src/components/command-palette.tsx:189
#: src/components/command-palette.tsx:204
msgid "Admin"
msgstr "Admin"
#: src/components/systems-table/systems-table.tsx:186
msgid "Agent"
msgstr "Agent"
#: src/components/alerts/alert-button.tsx:32
#: src/components/alerts/alert-button.tsx:68
msgid "Alerts"
msgstr "Warnungen"
#: src/components/alerts/alert-button.tsx:88
#: src/components/systems-table/systems-table.tsx:317
msgid "All Systems"
msgstr "Alle Systeme"
#: src/components/systems-table/systems-table.tsx:261
msgid "Are you sure you want to delete {name}?"
msgstr "Möchten Sie {name} wirklich löschen?"
#: src/components/command-palette.tsx:186
#: src/components/navbar.tsx:102
msgid "Auth Providers"
msgstr "Authentifizierungsanbieter"
#: src/components/copy-to-clipboard.tsx:16
msgid "Automatic copy requires a secure context."
msgstr "Automatisches Kopieren erfordert einen sicheren Kontext."
#: src/components/routes/system.tsx:568
msgid "Average"
msgstr "Durchschnitt"
#: src/components/routes/system.tsx:387
msgid "Average CPU utilization of containers"
msgstr "Durchschnittliche CPU-Auslastung der Container"
#: src/components/alerts/alerts-system.tsx:204
msgid "Average exceeds <0>{value}{0}</0>"
msgstr "Durchschnitt überschreitet <0>{value}{0}</0>"
#: src/components/routes/system.tsx:376
msgid "Average system-wide CPU utilization"
msgstr "Durchschnittliche systemweite CPU-Auslastung"
#: src/components/command-palette.tsx:171
#: src/components/navbar.tsx:94
msgid "Backups"
msgstr "Backups"
#: src/components/routes/system.tsx:436
#: src/lib/utils.ts:307
msgid "Bandwidth"
msgstr "Bandbreite"
#: src/components/login/auth-form.tsx:313
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
msgstr "Beszel unterstützt OpenID Connect und viele OAuth2-Authentifizierungsanbieter."
#: src/components/routes/settings/notifications.tsx:127
msgid "Beszel uses <0>Shoutrrr</0> to integrate with popular notification services."
msgstr "Beszel verwendet <0>Shoutrrr</0>, um sich mit beliebten Benachrichtigungsdiensten zu integrieren."
#: src/components/add-system.tsx:88
msgid "Binary"
msgstr "Binär"
#: src/components/charts/mem-chart.tsx:89
msgid "Cache / Buffers"
msgstr "Cache / Puffer"
#: src/components/systems-table/systems-table.tsx:272
msgid "Cancel"
msgstr "Abbrechen"
#: src/components/routes/settings/config-yaml.tsx:68
msgid "Caution - potential data loss"
msgstr "Vorsicht - potenzieller Datenverlust"
#: src/components/routes/settings/general.tsx:36
msgid "Change general application options."
msgstr "Allgemeine Anwendungsoptionen ändern."
#: src/components/routes/settings/general.tsx:78
msgid "Chart options"
msgstr "Diagrammoptionen"
#: src/components/login/forgot-pass-form.tsx:34
msgid "Check {email} for a reset link."
msgstr "Überprüfen Sie {email} auf einen Rücksetzlink."
#: src/components/routes/settings/layout.tsx:40
msgid "Check logs for more details."
msgstr "Überprüfen Sie die Protokolle für weitere Details."
#: src/components/routes/settings/notifications.tsx:183
msgid "Check your notification service"
msgstr "Überprüfen Sie Ihren Benachrichtigungsdienst"
#: src/components/add-system.tsx:153
msgid "Click to copy"
msgstr "Zum Kopieren klicken"
#. Context: table columns
#: src/components/systems-table/systems-table.tsx:328
msgid "Columns"
msgstr "Spalten"
#: src/components/login/forgot-pass-form.tsx:83
#: src/components/login/forgot-pass-form.tsx:89
msgid "Command line instructions"
msgstr "Befehlszeilenanweisungen"
#: src/components/routes/settings/notifications.tsx:77
msgid "Configure how you receive alert notifications."
msgstr "Konfigurieren Sie, wie Sie Warnbenachrichtigungen erhalten."
#: src/components/login/auth-form.tsx:189
#: src/components/login/auth-form.tsx:194
msgid "Confirm password"
msgstr "Passwort bestätigen"
#: src/components/systems-table/systems-table.tsx:278
msgid "Continue"
msgstr "Fortfahren"
#: src/lib/utils.ts:25
msgid "Copied to clipboard"
msgstr "In die Zwischenablage kopiert"
#: src/components/add-system.tsx:164
msgid "Copy"
msgstr "Kopieren"
#: src/components/systems-table/systems-table.tsx:247
msgid "Copy host"
msgstr "Host kopieren"
#: src/components/add-system.tsx:175
msgid "Copy Linux command"
msgstr "Linux-Befehl kopieren"
#: src/components/copy-to-clipboard.tsx:13
msgid "Copy text"
msgstr "Text kopieren"
#: src/components/systems-table/systems-table.tsx:152
msgid "CPU"
msgstr "CPU"
#: src/components/charts/area-chart.tsx:52
#: src/components/routes/system.tsx:375
#: src/lib/utils.ts:289
msgid "CPU Usage"
msgstr "CPU-Auslastung"
#: src/components/login/auth-form.tsx:215
msgid "Create account"
msgstr "Konto erstellen"
#. Dark theme
#: src/components/mode-toggle.tsx:21
msgid "Dark"
msgstr "Dunkel"
#: src/components/command-palette.tsx:82
#: src/components/routes/home.tsx:35
msgid "Dashboard"
msgstr "Dashboard"
#: src/components/routes/settings/general.tsx:85
msgid "Default time period"
msgstr "Standardzeitraum"
#: src/components/systems-table/systems-table.tsx:253
msgid "Delete"
msgstr "Löschen"
#: src/components/systems-table/systems-table.tsx:166
msgid "Disk"
msgstr "Festplatte"
#: src/components/routes/system.tsx:426
msgid "Disk I/O"
msgstr "Festplatten-I/O"
#: src/components/charts/disk-chart.tsx:74
#: src/components/routes/system.tsx:415
#: src/lib/utils.ts:301
msgid "Disk Usage"
msgstr "Festplattennutzung"
#: src/components/routes/system.tsx:495
msgid "Disk usage of {extraFsName}"
msgstr "Festplattennutzung von {extraFsName}"
#: src/components/routes/system.tsx:386
msgid "Docker CPU Usage"
msgstr "Docker-CPU-Auslastung"
#: src/components/routes/system.tsx:407
msgid "Docker Memory Usage"
msgstr "Docker-Speichernutzung"
#: src/components/routes/system.tsx:452
msgid "Docker Network I/O"
msgstr "Docker-Netzwerk-I/O"
#: src/components/command-palette.tsx:125
msgid "Documentation"
msgstr "Dokumentation"
#: src/components/login/auth-form.tsx:158
msgid "email"
msgstr "E-Mail"
#: src/components/login/auth-form.tsx:152
#: src/components/login/forgot-pass-form.tsx:53
msgid "Email"
msgstr "E-Mail"
#: src/components/routes/settings/notifications.tsx:91
msgid "Email notifications"
msgstr "E-Mail-Benachrichtigungen"
#: src/components/login/login.tsx:36
msgid "Enter email address to reset password"
msgstr "E-Mail-Adresse eingeben, um das Passwort zurückzusetzen"
#: src/components/routes/settings/notifications.tsx:111
msgid "Enter email address..."
msgstr "E-Mail-Adresse eingeben..."
#: src/components/login/auth-form.tsx:256
#: src/components/routes/settings/config-yaml.tsx:28
#: src/components/routes/settings/notifications.tsx:187
msgid "Error"
msgstr "Fehler"
#: src/components/routes/home.tsx:81
msgid "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
msgstr "Überschreitet {0}{1} in den letzten {2, plural, one {# Minute} other {# Minuten}}"
#: src/components/routes/settings/config-yaml.tsx:72
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
msgstr "Bestehende Systeme, die nicht in <0>config.yml</0> definiert sind, werden gelöscht. Bitte machen Sie regelmäßige Backups."
#: src/components/routes/settings/config-yaml.tsx:93
msgid "Export configuration"
msgstr "Konfiguration exportieren"
#: src/components/routes/settings/config-yaml.tsx:48
msgid "Export your current systems configuration."
msgstr "Exportieren Sie Ihre aktuelle Systemkonfiguration."
#: src/lib/utils.ts:38
msgid "Failed to authenticate"
msgstr "Authentifizierung fehlgeschlagen"
#: src/components/routes/settings/layout.tsx:39
#: src/components/routes/settings/notifications.tsx:62
msgid "Failed to save settings"
msgstr "Einstellungen konnten nicht gespeichert werden"
#: src/components/routes/settings/notifications.tsx:188
msgid "Failed to send test notification"
msgstr "Testbenachrichtigung konnte nicht gesendet werden"
#: src/components/alerts/alerts-system.tsx:27
msgid "Failed to update alert"
msgstr "Warnung konnte nicht aktualisiert werden"
#: src/components/routes/system.tsx:539
#: src/components/systems-table/systems-table.tsx:324
msgid "Filter..."
msgstr "Filter..."
#: src/components/alerts/alerts-system.tsx:225
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
msgstr "Für <0>{min}</0> {min, plural, one {Minute} other {Minuten}}"
#: src/components/login/auth-form.tsx:337
msgid "Forgot password?"
msgstr "Passwort vergessen?"
#. Context: General settings
#: src/components/routes/settings/general.tsx:33
#: src/components/routes/settings/layout.tsx:51
msgid "General"
msgstr "Allgemein"
#: src/components/add-system.tsx:119
msgid "Host / IP"
msgstr "Host / IP"
#: src/components/login/forgot-pass-form.tsx:93
msgid "If you've lost the password to your admin account, you may reset it using the following command."
msgstr "Wenn Sie das Passwort für Ihr Administratorkonto verloren haben, können Sie es mit dem folgenden Befehl zurücksetzen."
#: src/components/login/auth-form.tsx:16
msgid "Invalid email address."
msgstr "Ungültige E-Mail-Adresse."
#. Linux kernel
#: src/components/routes/system.tsx:254
msgid "Kernel"
msgstr "Kernel"
#: src/components/routes/settings/general.tsx:45
msgid "Language"
msgstr "Sprache"
#. Light theme
#: src/components/mode-toggle.tsx:16
msgid "Light"
msgstr "Hell"
#: src/components/navbar.tsx:113
msgid "Log Out"
msgstr "Abmelden"
#: src/components/login/login.tsx:17
msgid "Login"
msgstr "Anmelden"
#: src/components/login/auth-form.tsx:42
#: src/components/login/forgot-pass-form.tsx:15
msgid "Login attempt failed"
msgstr "Anmeldeversuch fehlgeschlagen"
#: src/components/command-palette.tsx:157
#: src/components/navbar.tsx:86
msgid "Logs"
msgstr "Protokolle"
#: src/components/routes/settings/notifications.tsx:80
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
msgstr "Suchen Sie stattdessen nach der Erstellung von Warnungen? Klicken Sie auf die Glocken-<0/>-Symbole in der Systemtabelle."
#: src/components/routes/settings/layout.tsx:85
msgid "Manage display and notification preferences."
msgstr "Anzeige- und Benachrichtigungseinstellungen verwalten."
#. Chart select field. Please try to keep this short.
#: src/components/routes/system.tsx:571
msgid "Max 1 min"
msgstr "Max 1 Min"
#: src/components/systems-table/systems-table.tsx:159
msgid "Memory"
msgstr "Speicher"
#: src/components/routes/system.tsx:397
#: src/lib/utils.ts:295
msgid "Memory Usage"
msgstr "Speichernutzung"
#: src/components/routes/system.tsx:408
msgid "Memory usage of docker containers"
msgstr "Speichernutzung der Docker-Container"
#: src/components/add-system.tsx:113
msgid "Name"
msgstr "Name"
#: src/components/systems-table/systems-table.tsx:173
msgid "Net"
msgstr "Netz"
#: src/components/routes/system.tsx:453
msgid "Network traffic of docker containers"
msgstr "Netzwerkverkehr der Docker-Container"
#: src/components/routes/system.tsx:438
msgid "Network traffic of public interfaces"
msgstr "Netzwerkverkehr der öffentlichen Schnittstellen"
#: src/components/command-palette.tsx:50
msgid "No results found."
msgstr "Keine Ergebnisse gefunden."
#: src/components/systems-table/systems-table.tsx:400
msgid "No systems found."
msgstr "Keine Systeme gefunden."
#: src/components/command-palette.tsx:111
#: src/components/routes/settings/layout.tsx:56
#: src/components/routes/settings/notifications.tsx:74
msgid "Notifications"
msgstr "Benachrichtigungen"
#: src/components/login/auth-form.tsx:308
msgid "OAuth 2 / OIDC support"
msgstr "OAuth 2 / OIDC-Unterstützung"
#: src/components/routes/settings/config-yaml.tsx:61
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
msgstr "Bei jedem Neustart werden die Systeme in der Datenbank aktualisiert, um den im Datei definierten Systemen zu entsprechen."
#: src/components/systems-table/systems-table.tsx:219
msgid "Open menu"
msgstr "Menü öffnen"
#: src/components/login/auth-form.tsx:227
msgid "Or continue with"
msgstr "Oder fortfahren mit"
#: src/components/alerts/alert-button.tsx:109
msgid "Overwrite existing alerts"
msgstr "Bestehende Warnungen überschreiben"
#: src/components/command-palette.tsx:85
msgid "Page"
msgstr "Seite"
#: src/components/command-palette.tsx:72
msgid "Pages / Settings"
msgstr "Seiten / Einstellungen"
#: src/components/login/auth-form.tsx:171
#: src/components/login/auth-form.tsx:176
msgid "Password"
msgstr "Passwort"
#: src/components/login/auth-form.tsx:17
msgid "Password must be at least 10 characters."
msgstr "Das Passwort muss mindestens 10 Zeichen lang sein."
#: src/components/login/forgot-pass-form.tsx:33
msgid "Password reset request received"
msgstr "Anfrage zum Zurücksetzen des Passworts erhalten"
#: src/components/systems-table/systems-table.tsx:241
msgid "Pause"
msgstr "Pause"
#: src/components/routes/settings/notifications.tsx:95
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
msgstr "Bitte <0>konfigurieren Sie einen SMTP-Server</0>, um sicherzustellen, dass Warnungen zugestellt werden."
#: src/components/alerts/alerts-system.tsx:28
msgid "Please check logs for more details."
msgstr "Bitte überprüfen Sie die Protokolle für weitere Details."
#: src/components/login/auth-form.tsx:43
#: src/components/login/forgot-pass-form.tsx:16
msgid "Please check your credentials and try again"
msgstr "Bitte überprüfen Sie Ihre Anmeldedaten und versuchen Sie es erneut"
#: src/components/login/login.tsx:34
msgid "Please create an admin account"
msgstr "Bitte erstellen Sie ein Administratorkonto"
#: src/components/login/auth-form.tsx:257
msgid "Please enable pop-ups for this site"
msgstr "Bitte aktivieren Sie Pop-ups für diese Seite"
#: src/lib/utils.ts:39
msgid "Please log in again"
msgstr "Bitte melden Sie sich erneut an"
#: src/components/login/auth-form.tsx:316
msgid "Please see <0>the documentation</0> for instructions."
msgstr "Bitte sehen Sie sich <0>die Dokumentation</0> für Anweisungen an."
#: src/components/login/login.tsx:38
msgid "Please sign in to your account"
msgstr "Bitte melden Sie sich bei Ihrem Konto an"
#: src/components/add-system.tsx:125
msgid "Port"
msgstr "Port"
#: src/components/routes/system.tsx:398
msgid "Precise utilization at the recorded time"
msgstr "Genaue Nutzung zum aufgezeichneten Zeitpunkt"
#: src/components/routes/settings/general.tsx:58
msgid "Preferred Language"
msgstr "Bevorzugte Sprache"
#. Use 'Key' if your language requires many more characters
#: src/components/add-system.tsx:131
msgid "Public Key"
msgstr "Schlüssel"
#. Context is disk read
#: src/components/charts/area-chart.tsx:56
#: src/components/charts/area-chart.tsx:66
msgid "Read"
msgstr "Lesen"
#. Context is network bytes received (download)
#: src/components/charts/area-chart.tsx:61
msgid "Received"
msgstr "Empfangen"
#: src/components/login/forgot-pass-form.tsx:76
msgid "Reset Password"
msgstr "Passwort zurücksetzen"
#: src/components/systems-table/systems-table.tsx:236
msgid "Resume"
msgstr "Fortsetzen"
#: src/components/routes/settings/notifications.tsx:117
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
msgstr "Adresse mit der Eingabetaste oder Komma speichern. Leer lassen, um E-Mail-Benachrichtigungen zu deaktivieren."
#: src/components/routes/settings/general.tsx:106
#: src/components/routes/settings/notifications.tsx:167
msgid "Save Settings"
msgstr "Einstellungen speichern"
#: src/components/navbar.tsx:142
msgid "Search"
msgstr "Suche"
#: src/components/command-palette.tsx:47
msgid "Search for systems or settings..."
msgstr "Nach Systemen oder Einstellungen suchen..."
#: src/components/alerts/alert-button.tsx:71
msgid "See <0>notification settings</0> to configure how you receive alerts."
msgstr "Siehe <0>Benachrichtigungseinstellungen</0>, um zu konfigurieren, wie Sie Warnungen erhalten."
#. Context is network bytes sent (upload)
#: src/components/charts/area-chart.tsx:60
msgid "Sent"
msgstr "Gesendet"
#: src/components/routes/settings/general.tsx:100
msgid "Sets the default time range for charts when a system is viewed."
msgstr "Legt den Standardzeitraum für Diagramme fest, wenn ein System angezeigt wird."
#: src/components/command-palette.tsx:96
#: src/components/command-palette.tsx:99
#: src/components/command-palette.tsx:114
#: src/components/routes/settings/layout.tsx:71
#: src/components/routes/settings/layout.tsx:82
msgid "Settings"
msgstr "Einstellungen"
#: src/components/routes/settings/layout.tsx:33
msgid "Settings saved"
msgstr "Einstellungen gespeichert"
#: src/components/login/auth-form.tsx:215
msgid "Sign in"
msgstr "Anmelden"
#: src/components/command-palette.tsx:201
msgid "SMTP settings"
msgstr "SMTP-Einstellungen"
#: src/lib/utils.ts:282
msgid "Status"
msgstr "Status"
#: src/components/routes/system.tsx:467
msgid "Swap space used by the system"
msgstr "Vom System genutzter Swap-Speicher"
#: src/components/routes/system.tsx:466
msgid "Swap Usage"
msgstr "Swap-Nutzung"
#. System theme
#: src/components/mode-toggle.tsx:26
#: src/components/systems-table/systems-table.tsx:110
#: src/components/systems-table/systems-table.tsx:121
msgid "System"
msgstr "System"
#: src/components/navbar.tsx:78
msgid "Systems"
msgstr "Systeme"
#: src/components/routes/settings/config-yaml.tsx:55
msgid "Systems may be managed in a <0>config.yml</0> file inside your data directory."
msgstr "Systeme können in einer <0>config.yml</0>-Datei in Ihrem Datenverzeichnis verwaltet werden."
#: src/components/routes/system.tsx:477
#: src/lib/utils.ts:314
msgid "Temperature"
msgstr "Temperatur"
#: src/components/routes/system.tsx:478
msgid "Temperatures of system sensors"
msgstr "Temperaturen der Systemsensoren"
#: src/components/routes/settings/notifications.tsx:211
msgid "Test <0>URL</0>"
msgstr "Test <0>URL</0>"
#: src/components/routes/settings/notifications.tsx:182
msgid "Test notification sent"
msgstr "Testbenachrichtigung gesendet"
#: src/components/add-system.tsx:104
msgid "The agent must be running on the system to connect. Copy the installation command for the agent below."
msgstr "Der Agent muss auf dem System laufen, um eine Verbindung herzustellen. Kopieren Sie den Installationsbefehl für den Agenten unten."
#: src/components/add-system.tsx:95
msgid "The agent must be running on the system to connect. Copy the<0>docker-compose.yml</0> for the agent below."
msgstr "Der Agent muss auf dem System laufen, um eine Verbindung herzustellen. Kopieren Sie die <0>docker-compose.yml</0> für den Agenten unten."
#: src/components/login/forgot-pass-form.tsx:98
msgid "Then log into the backend and reset your user account password in the users table."
msgstr "Melden Sie sich dann im Backend an und setzen Sie Ihr Benutzerkontopasswort in der Benutzertabelle zurück."
#: src/components/systems-table/systems-table.tsx:264
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
msgstr "Diese Aktion kann nicht rückgängig gemacht werden. Dadurch werden alle aktuellen Datensätze für {name} dauerhaft aus der Datenbank gelöscht."
#: src/components/routes/system.tsx:507
msgid "Throughput of {extraFsName}"
msgstr "Durchsatz von {extraFsName}"
#: src/components/routes/system.tsx:427
msgid "Throughput of root filesystem"
msgstr "Durchsatz des Root-Dateisystems"
#: src/components/routes/settings/notifications.tsx:106
msgid "To email(s)"
msgstr "An E-Mail(s)"
#: src/components/routes/system.tsx:350
#: src/components/routes/system.tsx:363
msgid "Toggle grid"
msgstr "Raster umschalten"
#: src/components/mode-toggle.tsx:33
msgid "Toggle theme"
msgstr "Thema umschalten"
#: src/lib/utils.ts:317
msgid "Triggers when any sensor exceeds a threshold"
msgstr "Löst aus, wenn ein Sensor einen Schwellenwert überschreitet"
#: src/lib/utils.ts:310
msgid "Triggers when combined up/down exceeds a threshold"
msgstr "Löst aus, wenn die kombinierte Auf-/Abwärtsbewegung einen Schwellenwert überschreitet"
#: src/lib/utils.ts:292
msgid "Triggers when CPU usage exceeds a threshold"
msgstr "Löst aus, wenn die CPU-Auslastung einen Schwellenwert überschreitet"
#: src/lib/utils.ts:298
msgid "Triggers when memory usage exceeds a threshold"
msgstr "Löst aus, wenn die Speichernutzung einen Schwellenwert überschreitet"
#: src/lib/utils.ts:285
msgid "Triggers when status switches between up and down"
msgstr "Löst aus, wenn der Status zwischen oben und unten wechselt"
#: src/lib/utils.ts:304
msgid "Triggers when usage of any disk exceeds a threshold"
msgstr "Löst aus, wenn die Nutzung einer Festplatte einen Schwellenwert überschreitet"
#: src/components/systems-table/systems-table.tsx:320
msgid "Updated in real time. Click on a system to view information."
msgstr "In Echtzeit aktualisiert. Klicken Sie auf ein System, um Informationen anzuzeigen."
#: src/components/routes/system.tsx:253
msgid "Uptime"
msgstr "Betriebszeit"
#: src/components/routes/system.tsx:494
msgid "Usage"
msgstr "Nutzung"
#: src/components/routes/system.tsx:415
msgid "Usage of root partition"
msgstr "Nutzung der Root-Partition"
#: src/components/charts/mem-chart.tsx:65
#: src/components/charts/swap-chart.tsx:56
msgid "Used"
msgstr "Verwendet"
#: src/components/login/auth-form.tsx:138
msgid "username"
msgstr "Benutzername"
#: src/components/login/auth-form.tsx:131
msgid "Username"
msgstr "Benutzername"
#: src/components/command-palette.tsx:143
#: src/components/navbar.tsx:70
msgid "Users"
msgstr "Benutzer"
#: src/components/routes/system.tsx:603
msgid "Waiting for enough records to display"
msgstr "Warten auf genügend Datensätze zur Anzeige"
#: src/components/routes/settings/general.tsx:48
msgid "Want to help us make our translations even better? Check out <0>Crowdin</0> for more details."
msgstr "Möchten Sie uns helfen, unsere Übersetzungen noch besser zu machen? Schauen Sie sich <0>Crowdin</0> für weitere Details an."
#: src/components/routes/settings/notifications.tsx:124
msgid "Webhook / Push notifications"
msgstr "Webhook / Push-Benachrichtigungen"
#. Context is disk write
#: src/components/charts/area-chart.tsx:55
#: src/components/charts/area-chart.tsx:65
msgid "Write"
msgstr "Schreiben"
#: src/components/routes/settings/layout.tsx:61
msgid "YAML Config"
msgstr "YAML-Konfiguration"
#: src/components/routes/settings/config-yaml.tsx:45
msgid "YAML Configuration"
msgstr "YAML-Konfiguration"
#: src/components/routes/settings/layout.tsx:34
msgid "Your user settings have been updated."
msgstr "Ihre Benutzereinstellungen wurden aktualisiert."

View File

@@ -0,0 +1,803 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2024-11-01 11:30-0400\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: en\n"
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
"Plural-Forms: \n"
#: src/components/routes/system.tsx:242
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# day} other {# days}}"
#: src/components/routes/system.tsx:240
msgid "{hours, plural, one {# hour} other {# hours}}"
msgstr "{hours, plural, one {# hour} other {# hours}}"
#: src/lib/utils.ts:139
msgid "1 hour"
msgstr "1 hour"
#: src/lib/utils.ts:162
msgid "1 week"
msgstr "1 week"
#: src/lib/utils.ts:147
msgid "12 hours"
msgstr "12 hours"
#: src/lib/utils.ts:155
msgid "24 hours"
msgstr "24 hours"
#: src/lib/utils.ts:170
msgid "30 days"
msgstr "30 days"
#. Table column
#: src/components/systems-table/systems-table.tsx:207
msgid "Actions"
msgstr "Actions"
#: src/components/routes/home.tsx:62
msgid "Active Alerts"
msgstr "Active Alerts"
#: src/components/add-system.tsx:74
msgid "Add <0>System</0>"
msgstr "Add <0>System</0>"
#: src/components/add-system.tsx:83
msgid "Add New System"
msgstr "Add New System"
#: src/components/add-system.tsx:167
#: src/components/add-system.tsx:178
msgid "Add system"
msgstr "Add system"
#: src/components/routes/settings/notifications.tsx:156
msgid "Add URL"
msgstr "Add URL"
#: src/components/routes/settings/general.tsx:81
msgid "Adjust display options for charts."
msgstr "Adjust display options for charts."
#: src/components/command-palette.tsx:133
#: src/components/command-palette.tsx:146
#: src/components/command-palette.tsx:160
#: src/components/command-palette.tsx:174
#: src/components/command-palette.tsx:189
#: src/components/command-palette.tsx:204
msgid "Admin"
msgstr "Admin"
#: src/components/systems-table/systems-table.tsx:186
msgid "Agent"
msgstr "Agent"
#: src/components/alerts/alert-button.tsx:32
#: src/components/alerts/alert-button.tsx:68
msgid "Alerts"
msgstr "Alerts"
#: src/components/alerts/alert-button.tsx:88
#: src/components/systems-table/systems-table.tsx:317
msgid "All Systems"
msgstr "All Systems"
#: src/components/systems-table/systems-table.tsx:261
msgid "Are you sure you want to delete {name}?"
msgstr "Are you sure you want to delete {name}?"
#: src/components/command-palette.tsx:186
#: src/components/navbar.tsx:102
msgid "Auth Providers"
msgstr "Auth Providers"
#: src/components/copy-to-clipboard.tsx:16
msgid "Automatic copy requires a secure context."
msgstr "Automatic copy requires a secure context."
#: src/components/routes/system.tsx:568
msgid "Average"
msgstr "Average"
#: src/components/routes/system.tsx:387
msgid "Average CPU utilization of containers"
msgstr "Average CPU utilization of containers"
#: src/components/alerts/alerts-system.tsx:204
msgid "Average exceeds <0>{value}{0}</0>"
msgstr "Average exceeds <0>{value}{0}</0>"
#: src/components/routes/system.tsx:376
msgid "Average system-wide CPU utilization"
msgstr "Average system-wide CPU utilization"
#: src/components/command-palette.tsx:171
#: src/components/navbar.tsx:94
msgid "Backups"
msgstr "Backups"
#: src/components/routes/system.tsx:436
#: src/lib/utils.ts:307
msgid "Bandwidth"
msgstr "Bandwidth"
#: src/components/login/auth-form.tsx:313
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
msgstr "Beszel supports OpenID Connect and many OAuth2 authentication providers."
#: src/components/routes/settings/notifications.tsx:127
msgid "Beszel uses <0>Shoutrrr</0> to integrate with popular notification services."
msgstr "Beszel uses <0>Shoutrrr</0> to integrate with popular notification services."
#: src/components/add-system.tsx:88
msgid "Binary"
msgstr "Binary"
#: src/components/charts/mem-chart.tsx:89
msgid "Cache / Buffers"
msgstr "Cache / Buffers"
#: src/components/systems-table/systems-table.tsx:272
msgid "Cancel"
msgstr "Cancel"
#: src/components/routes/settings/config-yaml.tsx:68
msgid "Caution - potential data loss"
msgstr "Caution - potential data loss"
#: src/components/routes/settings/general.tsx:36
msgid "Change general application options."
msgstr "Change general application options."
#: src/components/routes/settings/general.tsx:78
msgid "Chart options"
msgstr "Chart options"
#: src/components/login/forgot-pass-form.tsx:34
msgid "Check {email} for a reset link."
msgstr "Check {email} for a reset link."
#: src/components/routes/settings/layout.tsx:40
msgid "Check logs for more details."
msgstr "Check logs for more details."
#: src/components/routes/settings/notifications.tsx:183
msgid "Check your notification service"
msgstr "Check your notification service"
#: src/components/add-system.tsx:153
msgid "Click to copy"
msgstr "Click to copy"
#. Context: table columns
#: src/components/systems-table/systems-table.tsx:328
msgid "Columns"
msgstr "Columns"
#: src/components/login/forgot-pass-form.tsx:83
#: src/components/login/forgot-pass-form.tsx:89
msgid "Command line instructions"
msgstr "Command line instructions"
#: src/components/routes/settings/notifications.tsx:77
msgid "Configure how you receive alert notifications."
msgstr "Configure how you receive alert notifications."
#: src/components/login/auth-form.tsx:189
#: src/components/login/auth-form.tsx:194
msgid "Confirm password"
msgstr "Confirm password"
#: src/components/systems-table/systems-table.tsx:278
msgid "Continue"
msgstr "Continue"
#: src/lib/utils.ts:25
msgid "Copied to clipboard"
msgstr "Copied to clipboard"
#: src/components/add-system.tsx:164
msgid "Copy"
msgstr "Copy"
#: src/components/systems-table/systems-table.tsx:247
msgid "Copy host"
msgstr "Copy host"
#: src/components/add-system.tsx:175
msgid "Copy Linux command"
msgstr "Copy Linux command"
#: src/components/copy-to-clipboard.tsx:13
msgid "Copy text"
msgstr "Copy text"
#: src/components/systems-table/systems-table.tsx:152
msgid "CPU"
msgstr "CPU"
#: src/components/charts/area-chart.tsx:52
#: src/components/routes/system.tsx:375
#: src/lib/utils.ts:289
msgid "CPU Usage"
msgstr "CPU Usage"
#: src/components/login/auth-form.tsx:215
msgid "Create account"
msgstr "Create account"
#. Dark theme
#: src/components/mode-toggle.tsx:21
msgid "Dark"
msgstr "Dark"
#: src/components/command-palette.tsx:82
#: src/components/routes/home.tsx:35
msgid "Dashboard"
msgstr "Dashboard"
#: src/components/routes/settings/general.tsx:85
msgid "Default time period"
msgstr "Default time period"
#: src/components/systems-table/systems-table.tsx:253
msgid "Delete"
msgstr "Delete"
#: src/components/systems-table/systems-table.tsx:166
msgid "Disk"
msgstr "Disk"
#: src/components/routes/system.tsx:426
msgid "Disk I/O"
msgstr "Disk I/O"
#: src/components/charts/disk-chart.tsx:74
#: src/components/routes/system.tsx:415
#: src/lib/utils.ts:301
msgid "Disk Usage"
msgstr "Disk Usage"
#: src/components/routes/system.tsx:495
msgid "Disk usage of {extraFsName}"
msgstr "Disk usage of {extraFsName}"
#: src/components/routes/system.tsx:386
msgid "Docker CPU Usage"
msgstr "Docker CPU Usage"
#: src/components/routes/system.tsx:407
msgid "Docker Memory Usage"
msgstr "Docker Memory Usage"
#: src/components/routes/system.tsx:452
msgid "Docker Network I/O"
msgstr "Docker Network I/O"
#: src/components/command-palette.tsx:125
msgid "Documentation"
msgstr "Documentation"
#: src/components/login/auth-form.tsx:158
msgid "email"
msgstr "email"
#: src/components/login/auth-form.tsx:152
#: src/components/login/forgot-pass-form.tsx:53
msgid "Email"
msgstr "Email"
#: src/components/routes/settings/notifications.tsx:91
msgid "Email notifications"
msgstr "Email notifications"
#: src/components/login/login.tsx:36
msgid "Enter email address to reset password"
msgstr "Enter email address to reset password"
#: src/components/routes/settings/notifications.tsx:111
msgid "Enter email address..."
msgstr "Enter email address..."
#: src/components/login/auth-form.tsx:256
#: src/components/routes/settings/config-yaml.tsx:28
#: src/components/routes/settings/notifications.tsx:187
msgid "Error"
msgstr "Error"
#: src/components/routes/home.tsx:81
msgid "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
msgstr "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
#: src/components/routes/settings/config-yaml.tsx:72
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
msgstr "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
#: src/components/routes/settings/config-yaml.tsx:93
msgid "Export configuration"
msgstr "Export configuration"
#: src/components/routes/settings/config-yaml.tsx:48
msgid "Export your current systems configuration."
msgstr "Export your current systems configuration."
#: src/lib/utils.ts:38
msgid "Failed to authenticate"
msgstr "Failed to authenticate"
#: src/components/routes/settings/layout.tsx:39
#: src/components/routes/settings/notifications.tsx:62
msgid "Failed to save settings"
msgstr "Failed to save settings"
#: src/components/routes/settings/notifications.tsx:188
msgid "Failed to send test notification"
msgstr "Failed to send test notification"
#: src/components/alerts/alerts-system.tsx:27
msgid "Failed to update alert"
msgstr "Failed to update alert"
#: src/components/routes/system.tsx:539
#: src/components/systems-table/systems-table.tsx:324
msgid "Filter..."
msgstr "Filter..."
#: src/components/alerts/alerts-system.tsx:225
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
msgstr "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
#: src/components/login/auth-form.tsx:337
msgid "Forgot password?"
msgstr "Forgot password?"
#. Context: General settings
#: src/components/routes/settings/general.tsx:33
#: src/components/routes/settings/layout.tsx:51
msgid "General"
msgstr "General"
#: src/components/add-system.tsx:119
msgid "Host / IP"
msgstr "Host / IP"
#: src/components/login/forgot-pass-form.tsx:93
msgid "If you've lost the password to your admin account, you may reset it using the following command."
msgstr "If you've lost the password to your admin account, you may reset it using the following command."
#: src/components/login/auth-form.tsx:16
msgid "Invalid email address."
msgstr "Invalid email address."
#. Linux kernel
#: src/components/routes/system.tsx:254
msgid "Kernel"
msgstr "Kernel"
#: src/components/routes/settings/general.tsx:45
msgid "Language"
msgstr "Language"
#. Light theme
#: src/components/mode-toggle.tsx:16
msgid "Light"
msgstr "Light"
#: src/components/navbar.tsx:113
msgid "Log Out"
msgstr "Log Out"
#: src/components/login/login.tsx:17
msgid "Login"
msgstr "Login"
#: src/components/login/auth-form.tsx:42
#: src/components/login/forgot-pass-form.tsx:15
msgid "Login attempt failed"
msgstr "Login attempt failed"
#: src/components/command-palette.tsx:157
#: src/components/navbar.tsx:86
msgid "Logs"
msgstr "Logs"
#: src/components/routes/settings/notifications.tsx:80
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
msgstr "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
#: src/components/routes/settings/layout.tsx:85
msgid "Manage display and notification preferences."
msgstr "Manage display and notification preferences."
#. Chart select field. Please try to keep this short.
#: src/components/routes/system.tsx:571
msgid "Max 1 min"
msgstr "Max 1 min"
#: src/components/systems-table/systems-table.tsx:159
msgid "Memory"
msgstr "Memory"
#: src/components/routes/system.tsx:397
#: src/lib/utils.ts:295
msgid "Memory Usage"
msgstr "Memory Usage"
#: src/components/routes/system.tsx:408
msgid "Memory usage of docker containers"
msgstr "Memory usage of docker containers"
#: src/components/add-system.tsx:113
msgid "Name"
msgstr "Name"
#: src/components/systems-table/systems-table.tsx:173
msgid "Net"
msgstr "Net"
#: src/components/routes/system.tsx:453
msgid "Network traffic of docker containers"
msgstr "Network traffic of docker containers"
#: src/components/routes/system.tsx:438
msgid "Network traffic of public interfaces"
msgstr "Network traffic of public interfaces"
#: src/components/command-palette.tsx:50
msgid "No results found."
msgstr "No results found."
#: src/components/systems-table/systems-table.tsx:400
msgid "No systems found."
msgstr "No systems found."
#: src/components/command-palette.tsx:111
#: src/components/routes/settings/layout.tsx:56
#: src/components/routes/settings/notifications.tsx:74
msgid "Notifications"
msgstr "Notifications"
#: src/components/login/auth-form.tsx:308
msgid "OAuth 2 / OIDC support"
msgstr "OAuth 2 / OIDC support"
#: src/components/routes/settings/config-yaml.tsx:61
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
msgstr "On each restart, systems in the database will be updated to match the systems defined in the file."
#: src/components/systems-table/systems-table.tsx:219
msgid "Open menu"
msgstr "Open menu"
#: src/components/login/auth-form.tsx:227
msgid "Or continue with"
msgstr "Or continue with"
#: src/components/alerts/alert-button.tsx:109
msgid "Overwrite existing alerts"
msgstr "Overwrite existing alerts"
#: src/components/command-palette.tsx:85
msgid "Page"
msgstr "Page"
#: src/components/command-palette.tsx:72
msgid "Pages / Settings"
msgstr "Pages / Settings"
#: src/components/login/auth-form.tsx:171
#: src/components/login/auth-form.tsx:176
msgid "Password"
msgstr "Password"
#: src/components/login/auth-form.tsx:17
msgid "Password must be at least 10 characters."
msgstr "Password must be at least 10 characters."
#: src/components/login/forgot-pass-form.tsx:33
msgid "Password reset request received"
msgstr "Password reset request received"
#: src/components/systems-table/systems-table.tsx:241
msgid "Pause"
msgstr "Pause"
#: src/components/routes/settings/notifications.tsx:95
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
msgstr "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
#: src/components/alerts/alerts-system.tsx:28
msgid "Please check logs for more details."
msgstr "Please check logs for more details."
#: src/components/login/auth-form.tsx:43
#: src/components/login/forgot-pass-form.tsx:16
msgid "Please check your credentials and try again"
msgstr "Please check your credentials and try again"
#: src/components/login/login.tsx:34
msgid "Please create an admin account"
msgstr "Please create an admin account"
#: src/components/login/auth-form.tsx:257
msgid "Please enable pop-ups for this site"
msgstr "Please enable pop-ups for this site"
#: src/lib/utils.ts:39
msgid "Please log in again"
msgstr "Please log in again"
#: src/components/login/auth-form.tsx:316
msgid "Please see <0>the documentation</0> for instructions."
msgstr "Please see <0>the documentation</0> for instructions."
#: src/components/login/login.tsx:38
msgid "Please sign in to your account"
msgstr "Please sign in to your account"
#: src/components/add-system.tsx:125
msgid "Port"
msgstr "Port"
#: src/components/routes/system.tsx:398
msgid "Precise utilization at the recorded time"
msgstr "Precise utilization at the recorded time"
#: src/components/routes/settings/general.tsx:58
msgid "Preferred Language"
msgstr "Preferred Language"
#. Use 'Key' if your language requires many more characters
#: src/components/add-system.tsx:131
msgid "Public Key"
msgstr "Public Key"
#. Context is disk read
#: src/components/charts/area-chart.tsx:56
#: src/components/charts/area-chart.tsx:66
msgid "Read"
msgstr "Read"
#. Context is network bytes received (download)
#: src/components/charts/area-chart.tsx:61
msgid "Received"
msgstr "Received"
#: src/components/login/forgot-pass-form.tsx:76
msgid "Reset Password"
msgstr "Reset Password"
#: src/components/systems-table/systems-table.tsx:236
msgid "Resume"
msgstr "Resume"
#: src/components/routes/settings/notifications.tsx:117
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
msgstr "Save address using enter key or comma. Leave blank to disable email notifications."
#: src/components/routes/settings/general.tsx:106
#: src/components/routes/settings/notifications.tsx:167
msgid "Save Settings"
msgstr "Save Settings"
#: src/components/navbar.tsx:142
msgid "Search"
msgstr "Search"
#: src/components/command-palette.tsx:47
msgid "Search for systems or settings..."
msgstr "Search for systems or settings..."
#: src/components/alerts/alert-button.tsx:71
msgid "See <0>notification settings</0> to configure how you receive alerts."
msgstr "See <0>notification settings</0> to configure how you receive alerts."
#. Context is network bytes sent (upload)
#: src/components/charts/area-chart.tsx:60
msgid "Sent"
msgstr "Sent"
#: src/components/routes/settings/general.tsx:100
msgid "Sets the default time range for charts when a system is viewed."
msgstr "Sets the default time range for charts when a system is viewed."
#: src/components/command-palette.tsx:96
#: src/components/command-palette.tsx:99
#: src/components/command-palette.tsx:114
#: src/components/routes/settings/layout.tsx:71
#: src/components/routes/settings/layout.tsx:82
msgid "Settings"
msgstr "Settings"
#: src/components/routes/settings/layout.tsx:33
msgid "Settings saved"
msgstr "Settings saved"
#: src/components/login/auth-form.tsx:215
msgid "Sign in"
msgstr "Sign in"
#: src/components/command-palette.tsx:201
msgid "SMTP settings"
msgstr "SMTP settings"
#: src/lib/utils.ts:282
msgid "Status"
msgstr "Status"
#: src/components/routes/system.tsx:467
msgid "Swap space used by the system"
msgstr "Swap space used by the system"
#: src/components/routes/system.tsx:466
msgid "Swap Usage"
msgstr "Swap Usage"
#. System theme
#: src/components/mode-toggle.tsx:26
#: src/components/systems-table/systems-table.tsx:110
#: src/components/systems-table/systems-table.tsx:121
msgid "System"
msgstr "System"
#: src/components/navbar.tsx:78
msgid "Systems"
msgstr "Systems"
#: src/components/routes/settings/config-yaml.tsx:55
msgid "Systems may be managed in a <0>config.yml</0> file inside your data directory."
msgstr "Systems may be managed in a <0>config.yml</0> file inside your data directory."
#: src/components/routes/system.tsx:477
#: src/lib/utils.ts:314
msgid "Temperature"
msgstr "Temperature"
#: src/components/routes/system.tsx:478
msgid "Temperatures of system sensors"
msgstr "Temperatures of system sensors"
#: src/components/routes/settings/notifications.tsx:211
msgid "Test <0>URL</0>"
msgstr "Test <0>URL</0>"
#: src/components/routes/settings/notifications.tsx:182
msgid "Test notification sent"
msgstr "Test notification sent"
#: src/components/add-system.tsx:104
msgid "The agent must be running on the system to connect. Copy the installation command for the agent below."
msgstr "The agent must be running on the system to connect. Copy the installation command for the agent below."
#: src/components/add-system.tsx:95
msgid "The agent must be running on the system to connect. Copy the<0>docker-compose.yml</0> for the agent below."
msgstr "The agent must be running on the system to connect. Copy the<0>docker-compose.yml</0> for the agent below."
#: src/components/login/forgot-pass-form.tsx:98
msgid "Then log into the backend and reset your user account password in the users table."
msgstr "Then log into the backend and reset your user account password in the users table."
#: src/components/systems-table/systems-table.tsx:264
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
msgstr "This action cannot be undone. This will permanently delete all current records for {name} from the database."
#: src/components/routes/system.tsx:507
msgid "Throughput of {extraFsName}"
msgstr "Throughput of {extraFsName}"
#: src/components/routes/system.tsx:427
msgid "Throughput of root filesystem"
msgstr "Throughput of root filesystem"
#: src/components/routes/settings/notifications.tsx:106
msgid "To email(s)"
msgstr "To email(s)"
#: src/components/routes/system.tsx:350
#: src/components/routes/system.tsx:363
msgid "Toggle grid"
msgstr "Toggle grid"
#: src/components/mode-toggle.tsx:33
msgid "Toggle theme"
msgstr "Toggle theme"
#: src/lib/utils.ts:317
msgid "Triggers when any sensor exceeds a threshold"
msgstr "Triggers when any sensor exceeds a threshold"
#: src/lib/utils.ts:310
msgid "Triggers when combined up/down exceeds a threshold"
msgstr "Triggers when combined up/down exceeds a threshold"
#: src/lib/utils.ts:292
msgid "Triggers when CPU usage exceeds a threshold"
msgstr "Triggers when CPU usage exceeds a threshold"
#: src/lib/utils.ts:298
msgid "Triggers when memory usage exceeds a threshold"
msgstr "Triggers when memory usage exceeds a threshold"
#: src/lib/utils.ts:285
msgid "Triggers when status switches between up and down"
msgstr "Triggers when status switches between up and down"
#: src/lib/utils.ts:304
msgid "Triggers when usage of any disk exceeds a threshold"
msgstr "Triggers when usage of any disk exceeds a threshold"
#: src/components/systems-table/systems-table.tsx:320
msgid "Updated in real time. Click on a system to view information."
msgstr "Updated in real time. Click on a system to view information."
#: src/components/routes/system.tsx:253
msgid "Uptime"
msgstr "Uptime"
#: src/components/routes/system.tsx:494
msgid "Usage"
msgstr "Usage"
#: src/components/routes/system.tsx:415
msgid "Usage of root partition"
msgstr "Usage of root partition"
#: src/components/charts/mem-chart.tsx:65
#: src/components/charts/swap-chart.tsx:56
msgid "Used"
msgstr "Used"
#: src/components/login/auth-form.tsx:138
msgid "username"
msgstr "username"
#: src/components/login/auth-form.tsx:131
msgid "Username"
msgstr "Username"
#: src/components/command-palette.tsx:143
#: src/components/navbar.tsx:70
msgid "Users"
msgstr "Users"
#: src/components/routes/system.tsx:603
msgid "Waiting for enough records to display"
msgstr "Waiting for enough records to display"
#: src/components/routes/settings/general.tsx:48
msgid "Want to help us make our translations even better? Check out <0>Crowdin</0> for more details."
msgstr "Want to help us make our translations even better? Check out <0>Crowdin</0> for more details."
#: src/components/routes/settings/notifications.tsx:124
msgid "Webhook / Push notifications"
msgstr "Webhook / Push notifications"
#. Context is disk write
#: src/components/charts/area-chart.tsx:55
#: src/components/charts/area-chart.tsx:65
msgid "Write"
msgstr "Write"
#: src/components/routes/settings/layout.tsx:61
msgid "YAML Config"
msgstr "YAML Config"
#: src/components/routes/settings/config-yaml.tsx:45
msgid "YAML Configuration"
msgstr "YAML Configuration"
#: src/components/routes/settings/layout.tsx:34
msgid "Your user settings have been updated."
msgstr "Your user settings have been updated."

View File

@@ -0,0 +1,808 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2024-11-01 11:30-0400\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: es\n"
"Project-Id-Version: beszel\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-11-04 20:46\n"
"Last-Translator: \n"
"Language-Team: Spanish\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: beszel\n"
"X-Crowdin-Project-ID: 733311\n"
"X-Crowdin-Language: es-ES\n"
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 16\n"
#: src/components/routes/system.tsx:242
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# día} other {# días}}"
#: src/components/routes/system.tsx:240
msgid "{hours, plural, one {# hour} other {# hours}}"
msgstr "{hours, plural, one {# hora} other {# horas}}"
#: src/lib/utils.ts:139
msgid "1 hour"
msgstr "1 hora"
#: src/lib/utils.ts:162
msgid "1 week"
msgstr "1 semana"
#: src/lib/utils.ts:147
msgid "12 hours"
msgstr "12 horas"
#: src/lib/utils.ts:155
msgid "24 hours"
msgstr "24 horas"
#: src/lib/utils.ts:170
msgid "30 days"
msgstr "30 días"
#. Table column
#: src/components/systems-table/systems-table.tsx:207
msgid "Actions"
msgstr "Acciones"
#: src/components/routes/home.tsx:62
msgid "Active Alerts"
msgstr "Alertas Activas"
#: src/components/add-system.tsx:74
msgid "Add <0>System</0>"
msgstr "Agregar <0>Sistema</0>"
#: src/components/add-system.tsx:83
msgid "Add New System"
msgstr "Agregar Nuevo Sistema"
#: src/components/add-system.tsx:167
#: src/components/add-system.tsx:178
msgid "Add system"
msgstr "Agregar sistema"
#: src/components/routes/settings/notifications.tsx:156
msgid "Add URL"
msgstr "Agregar URL"
#: src/components/routes/settings/general.tsx:81
msgid "Adjust display options for charts."
msgstr "Ajustar las opciones de visualización para los gráficos."
#: src/components/command-palette.tsx:133
#: src/components/command-palette.tsx:146
#: src/components/command-palette.tsx:160
#: src/components/command-palette.tsx:174
#: src/components/command-palette.tsx:189
#: src/components/command-palette.tsx:204
msgid "Admin"
msgstr "Administrador"
#: src/components/systems-table/systems-table.tsx:186
msgid "Agent"
msgstr "Agente"
#: src/components/alerts/alert-button.tsx:32
#: src/components/alerts/alert-button.tsx:68
msgid "Alerts"
msgstr "Alertas"
#: src/components/alerts/alert-button.tsx:88
#: src/components/systems-table/systems-table.tsx:317
msgid "All Systems"
msgstr "Todos los Sistemas"
#: src/components/systems-table/systems-table.tsx:261
msgid "Are you sure you want to delete {name}?"
msgstr "¿Está seguro de que desea eliminar {name}?"
#: src/components/command-palette.tsx:186
#: src/components/navbar.tsx:102
msgid "Auth Providers"
msgstr "Proveedores de Autenticación"
#: src/components/copy-to-clipboard.tsx:16
msgid "Automatic copy requires a secure context."
msgstr "La copia automática requiere un contexto seguro."
#: src/components/routes/system.tsx:568
msgid "Average"
msgstr "Promedio"
#: src/components/routes/system.tsx:387
msgid "Average CPU utilization of containers"
msgstr "Utilización promedio de CPU de los contenedores"
#: src/components/alerts/alerts-system.tsx:204
msgid "Average exceeds <0>{value}{0}</0>"
msgstr "El promedio excede <0>{value}{0}</0>"
#: src/components/routes/system.tsx:376
msgid "Average system-wide CPU utilization"
msgstr "Utilización promedio de CPU del sistema"
#: src/components/command-palette.tsx:171
#: src/components/navbar.tsx:94
msgid "Backups"
msgstr "Copias de Seguridad"
#: src/components/routes/system.tsx:436
#: src/lib/utils.ts:307
msgid "Bandwidth"
msgstr "Ancho de banda"
#: src/components/login/auth-form.tsx:313
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
msgstr "Beszel admite OpenID Connect y muchos proveedores de autenticación OAuth2."
#: src/components/routes/settings/notifications.tsx:127
msgid "Beszel uses <0>Shoutrrr</0> to integrate with popular notification services."
msgstr "Beszel utiliza <0>Shoutrrr</0> para integrarse con servicios populares de notificación."
#: src/components/add-system.tsx:88
msgid "Binary"
msgstr "Binario"
#: src/components/charts/mem-chart.tsx:89
msgid "Cache / Buffers"
msgstr "Caché / Buffers"
#: src/components/systems-table/systems-table.tsx:272
msgid "Cancel"
msgstr "Cancelar"
#: src/components/routes/settings/config-yaml.tsx:68
msgid "Caution - potential data loss"
msgstr "Precaución - posible pérdida de datos"
#: src/components/routes/settings/general.tsx:36
msgid "Change general application options."
msgstr "Cambiar las opciones generales de la aplicación."
#: src/components/routes/settings/general.tsx:78
msgid "Chart options"
msgstr "Opciones de Gráficos"
#: src/components/login/forgot-pass-form.tsx:34
msgid "Check {email} for a reset link."
msgstr "Revise {email} para un enlace de restablecimiento."
#: src/components/routes/settings/layout.tsx:40
msgid "Check logs for more details."
msgstr "Revise los registros para más detalles."
#: src/components/routes/settings/notifications.tsx:183
msgid "Check your notification service"
msgstr "Verifique su servicio de notificaciones"
#: src/components/add-system.tsx:153
msgid "Click to copy"
msgstr "Haga clic para copiar"
#. Context: table columns
#: src/components/systems-table/systems-table.tsx:328
msgid "Columns"
msgstr "Columnas"
#: src/components/login/forgot-pass-form.tsx:83
#: src/components/login/forgot-pass-form.tsx:89
msgid "Command line instructions"
msgstr "Instrucciones de línea de comandos"
#: src/components/routes/settings/notifications.tsx:77
msgid "Configure how you receive alert notifications."
msgstr "Configure cómo recibe las notificaciones de alertas."
#: src/components/login/auth-form.tsx:189
#: src/components/login/auth-form.tsx:194
msgid "Confirm password"
msgstr "Confirmar contraseña"
#: src/components/systems-table/systems-table.tsx:278
msgid "Continue"
msgstr "Continuar"
#: src/lib/utils.ts:25
msgid "Copied to clipboard"
msgstr "Copiado al portapapeles"
#: src/components/add-system.tsx:164
msgid "Copy"
msgstr "Copiar"
#: src/components/systems-table/systems-table.tsx:247
msgid "Copy host"
msgstr "Copiar host"
#: src/components/add-system.tsx:175
msgid "Copy Linux command"
msgstr "Copiar comando de Linux"
#: src/components/copy-to-clipboard.tsx:13
msgid "Copy text"
msgstr "Copiar texto"
#: src/components/systems-table/systems-table.tsx:152
msgid "CPU"
msgstr "CPU"
#: src/components/charts/area-chart.tsx:52
#: src/components/routes/system.tsx:375
#: src/lib/utils.ts:289
msgid "CPU Usage"
msgstr "Uso de CPU"
#: src/components/login/auth-form.tsx:215
msgid "Create account"
msgstr "Crear cuenta"
#. Dark theme
#: src/components/mode-toggle.tsx:21
msgid "Dark"
msgstr "Oscuro"
#: src/components/command-palette.tsx:82
#: src/components/routes/home.tsx:35
msgid "Dashboard"
msgstr "Tablero"
#: src/components/routes/settings/general.tsx:85
msgid "Default time period"
msgstr "Período de tiempo predeterminado"
#: src/components/systems-table/systems-table.tsx:253
msgid "Delete"
msgstr "Eliminar"
#: src/components/systems-table/systems-table.tsx:166
msgid "Disk"
msgstr "Disco"
#: src/components/routes/system.tsx:426
msgid "Disk I/O"
msgstr "E/S de Disco"
#: src/components/charts/disk-chart.tsx:74
#: src/components/routes/system.tsx:415
#: src/lib/utils.ts:301
msgid "Disk Usage"
msgstr "Uso de Disco"
#: src/components/routes/system.tsx:495
msgid "Disk usage of {extraFsName}"
msgstr "Uso de disco de {extraFsName}"
#: src/components/routes/system.tsx:386
msgid "Docker CPU Usage"
msgstr "Uso de CPU de Docker"
#: src/components/routes/system.tsx:407
msgid "Docker Memory Usage"
msgstr "Uso de Memoria de Docker"
#: src/components/routes/system.tsx:452
msgid "Docker Network I/O"
msgstr "E/S de Red de Docker"
#: src/components/command-palette.tsx:125
msgid "Documentation"
msgstr "Documentación"
#: src/components/login/auth-form.tsx:158
msgid "email"
msgstr "correo electrónico"
#: src/components/login/auth-form.tsx:152
#: src/components/login/forgot-pass-form.tsx:53
msgid "Email"
msgstr "Correo electrónico"
#: src/components/routes/settings/notifications.tsx:91
msgid "Email notifications"
msgstr "Notificaciones por correo"
#: src/components/login/login.tsx:36
msgid "Enter email address to reset password"
msgstr "Ingrese la dirección de correo electrónico para restablecer la contraseña"
#: src/components/routes/settings/notifications.tsx:111
msgid "Enter email address..."
msgstr "Ingrese dirección de correo..."
#: src/components/login/auth-form.tsx:256
#: src/components/routes/settings/config-yaml.tsx:28
#: src/components/routes/settings/notifications.tsx:187
msgid "Error"
msgstr "Error"
#: src/components/routes/home.tsx:81
msgid "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
msgstr "Excede {0}{1} en el último {2, plural, one {# minuto} other {# minutos}}"
#: src/components/routes/settings/config-yaml.tsx:72
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
msgstr "Los sistemas existentes no definidos en <0>config.yml</0> serán eliminados. Por favor, haga copias de seguridad regularmente."
#: src/components/routes/settings/config-yaml.tsx:93
msgid "Export configuration"
msgstr "Exportar configuración"
#: src/components/routes/settings/config-yaml.tsx:48
msgid "Export your current systems configuration."
msgstr "Exporte la configuración actual de sus sistemas."
#: src/lib/utils.ts:38
msgid "Failed to authenticate"
msgstr "Error al autenticar"
#: src/components/routes/settings/layout.tsx:39
#: src/components/routes/settings/notifications.tsx:62
msgid "Failed to save settings"
msgstr "Error al guardar la configuración"
#: src/components/routes/settings/notifications.tsx:188
msgid "Failed to send test notification"
msgstr "Error al enviar la notificación de prueba"
#: src/components/alerts/alerts-system.tsx:27
msgid "Failed to update alert"
msgstr "Error al actualizar la alerta"
#: src/components/routes/system.tsx:539
#: src/components/systems-table/systems-table.tsx:324
msgid "Filter..."
msgstr "Filtrar..."
#: src/components/alerts/alerts-system.tsx:225
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
msgstr "Por <0>{min}</0> {min, plural, one {minuto} other {minutos}}"
#: src/components/login/auth-form.tsx:337
msgid "Forgot password?"
msgstr "¿Olvidó su contraseña?"
#. Context: General settings
#: src/components/routes/settings/general.tsx:33
#: src/components/routes/settings/layout.tsx:51
msgid "General"
msgstr "General"
#: src/components/add-system.tsx:119
msgid "Host / IP"
msgstr "Host / IP"
#: src/components/login/forgot-pass-form.tsx:93
msgid "If you've lost the password to your admin account, you may reset it using the following command."
msgstr "Si ha perdido la contraseña de su cuenta de administrador, puede restablecerla usando el siguiente comando."
#: src/components/login/auth-form.tsx:16
msgid "Invalid email address."
msgstr "Dirección de correo electrónico no válida."
#. Linux kernel
#: src/components/routes/system.tsx:254
msgid "Kernel"
msgstr "Kernel"
#: src/components/routes/settings/general.tsx:45
msgid "Language"
msgstr "Idioma"
#. Light theme
#: src/components/mode-toggle.tsx:16
msgid "Light"
msgstr "Claro"
#: src/components/navbar.tsx:113
msgid "Log Out"
msgstr "Cerrar Sesión"
#: src/components/login/login.tsx:17
msgid "Login"
msgstr "Iniciar sesión"
#: src/components/login/auth-form.tsx:42
#: src/components/login/forgot-pass-form.tsx:15
msgid "Login attempt failed"
msgstr "Intento de inicio de sesión fallido"
#: src/components/command-palette.tsx:157
#: src/components/navbar.tsx:86
msgid "Logs"
msgstr "Registros"
#: src/components/routes/settings/notifications.tsx:80
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
msgstr "¿Busca dónde crear alertas? Haga clic en los iconos de campana <0/> en la tabla de sistemas."
#: src/components/routes/settings/layout.tsx:85
msgid "Manage display and notification preferences."
msgstr "Administrar preferencias de visualización y notificaciones."
#. Chart select field. Please try to keep this short.
#: src/components/routes/system.tsx:571
msgid "Max 1 min"
msgstr "Máx 1 min"
#: src/components/systems-table/systems-table.tsx:159
msgid "Memory"
msgstr "Memoria"
#: src/components/routes/system.tsx:397
#: src/lib/utils.ts:295
msgid "Memory Usage"
msgstr "Uso de Memoria"
#: src/components/routes/system.tsx:408
msgid "Memory usage of docker containers"
msgstr "Uso de memoria de los contenedores de Docker"
#: src/components/add-system.tsx:113
msgid "Name"
msgstr "Nombre"
#: src/components/systems-table/systems-table.tsx:173
msgid "Net"
msgstr "Red"
#: src/components/routes/system.tsx:453
msgid "Network traffic of docker containers"
msgstr "Tráfico de red de los contenedores de Docker"
#: src/components/routes/system.tsx:438
msgid "Network traffic of public interfaces"
msgstr "Tráfico de red de interfaces públicas"
#: src/components/command-palette.tsx:50
msgid "No results found."
msgstr "No se encontraron resultados."
#: src/components/systems-table/systems-table.tsx:400
msgid "No systems found."
msgstr "No se encontraron sistemas."
#: src/components/command-palette.tsx:111
#: src/components/routes/settings/layout.tsx:56
#: src/components/routes/settings/notifications.tsx:74
msgid "Notifications"
msgstr "Notificaciones"
#: src/components/login/auth-form.tsx:308
msgid "OAuth 2 / OIDC support"
msgstr "Soporte para OAuth 2 / OIDC"
#: src/components/routes/settings/config-yaml.tsx:61
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
msgstr "En cada reinicio, los sistemas en la base de datos se actualizarán para coincidir con los sistemas definidos en el archivo."
#: src/components/systems-table/systems-table.tsx:219
msgid "Open menu"
msgstr "Abrir menú"
#: src/components/login/auth-form.tsx:227
msgid "Or continue with"
msgstr "O continuar con"
#: src/components/alerts/alert-button.tsx:109
msgid "Overwrite existing alerts"
msgstr "Sobrescribir alertas existentes"
#: src/components/command-palette.tsx:85
msgid "Page"
msgstr "Página"
#: src/components/command-palette.tsx:72
msgid "Pages / Settings"
msgstr "Páginas / Configuraciones"
#: src/components/login/auth-form.tsx:171
#: src/components/login/auth-form.tsx:176
msgid "Password"
msgstr "Contraseña"
#: src/components/login/auth-form.tsx:17
msgid "Password must be at least 10 characters."
msgstr "La contraseña debe tener al menos 10 caracteres."
#: src/components/login/forgot-pass-form.tsx:33
msgid "Password reset request received"
msgstr "Solicitud de restablecimiento de contraseña recibida"
#: src/components/systems-table/systems-table.tsx:241
msgid "Pause"
msgstr "Pausar"
#: src/components/routes/settings/notifications.tsx:95
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
msgstr "Por favor, <0>configure un servidor SMTP</0> para asegurar que las alertas sean entregadas."
#: src/components/alerts/alerts-system.tsx:28
msgid "Please check logs for more details."
msgstr "Por favor, revise los registros para más detalles."
#: src/components/login/auth-form.tsx:43
#: src/components/login/forgot-pass-form.tsx:16
msgid "Please check your credentials and try again"
msgstr "Por favor, verifique sus credenciales e intente de nuevo"
#: src/components/login/login.tsx:34
msgid "Please create an admin account"
msgstr "Por favor, cree una cuenta de administrador"
#: src/components/login/auth-form.tsx:257
msgid "Please enable pop-ups for this site"
msgstr "Por favor, habilite las ventanas emergentes para este sitio"
#: src/lib/utils.ts:39
msgid "Please log in again"
msgstr "Por favor, inicie sesión de nuevo"
#: src/components/login/auth-form.tsx:316
msgid "Please see <0>the documentation</0> for instructions."
msgstr "Por favor, consulte <0>la documentación</0> para obtener instrucciones."
#: src/components/login/login.tsx:38
msgid "Please sign in to your account"
msgstr "Por favor, inicie sesión en su cuenta"
#: src/components/add-system.tsx:125
msgid "Port"
msgstr "Puerto"
#: src/components/routes/system.tsx:398
msgid "Precise utilization at the recorded time"
msgstr "Utilización precisa en el momento registrado"
#: src/components/routes/settings/general.tsx:58
msgid "Preferred Language"
msgstr "Idioma Preferido"
#. Use 'Key' if your language requires many more characters
#: src/components/add-system.tsx:131
msgid "Public Key"
msgstr "Clave Pública"
#. Context is disk read
#: src/components/charts/area-chart.tsx:56
#: src/components/charts/area-chart.tsx:66
msgid "Read"
msgstr "Lectura"
#. Context is network bytes received (download)
#: src/components/charts/area-chart.tsx:61
msgid "Received"
msgstr "Recibido"
#: src/components/login/forgot-pass-form.tsx:76
msgid "Reset Password"
msgstr "Restablecer Contraseña"
#: src/components/systems-table/systems-table.tsx:236
msgid "Resume"
msgstr "Reanudar"
#: src/components/routes/settings/notifications.tsx:117
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
msgstr "Guarde la dirección usando la tecla enter o coma. Deje en blanco para desactivar las notificaciones por correo."
#: src/components/routes/settings/general.tsx:106
#: src/components/routes/settings/notifications.tsx:167
msgid "Save Settings"
msgstr "Guardar Configuración"
#: src/components/navbar.tsx:142
msgid "Search"
msgstr "Buscar"
#: src/components/command-palette.tsx:47
msgid "Search for systems or settings..."
msgstr "Buscar sistemas o configuraciones..."
#: src/components/alerts/alert-button.tsx:71
msgid "See <0>notification settings</0> to configure how you receive alerts."
msgstr "Consulte <0>configuración de notificaciones</0> para configurar cómo recibe alertas."
#. Context is network bytes sent (upload)
#: src/components/charts/area-chart.tsx:60
msgid "Sent"
msgstr "Enviado"
#: src/components/routes/settings/general.tsx:100
msgid "Sets the default time range for charts when a system is viewed."
msgstr "Establece el rango de tiempo predeterminado para los gráficos cuando se visualiza un sistema."
#: src/components/command-palette.tsx:96
#: src/components/command-palette.tsx:99
#: src/components/command-palette.tsx:114
#: src/components/routes/settings/layout.tsx:71
#: src/components/routes/settings/layout.tsx:82
msgid "Settings"
msgstr "Configuración"
#: src/components/routes/settings/layout.tsx:33
msgid "Settings saved"
msgstr "Configuración guardada"
#: src/components/login/auth-form.tsx:215
msgid "Sign in"
msgstr "Iniciar sesión"
#: src/components/command-palette.tsx:201
msgid "SMTP settings"
msgstr "Configuración SMTP"
#: src/lib/utils.ts:282
msgid "Status"
msgstr "Estado"
#: src/components/routes/system.tsx:467
msgid "Swap space used by the system"
msgstr "Espacio de swap utilizado por el sistema"
#: src/components/routes/system.tsx:466
msgid "Swap Usage"
msgstr "Uso de Swap"
#. System theme
#: src/components/mode-toggle.tsx:26
#: src/components/systems-table/systems-table.tsx:110
#: src/components/systems-table/systems-table.tsx:121
msgid "System"
msgstr "Sistema"
#: src/components/navbar.tsx:78
msgid "Systems"
msgstr "Sistemas"
#: src/components/routes/settings/config-yaml.tsx:55
msgid "Systems may be managed in a <0>config.yml</0> file inside your data directory."
msgstr "Los sistemas pueden ser gestionados en un archivo <0>config.yml</0> dentro de su directorio de datos."
#: src/components/routes/system.tsx:477
#: src/lib/utils.ts:314
msgid "Temperature"
msgstr "Temperatura"
#: src/components/routes/system.tsx:478
msgid "Temperatures of system sensors"
msgstr "Temperaturas de los sensores del sistema"
#: src/components/routes/settings/notifications.tsx:211
msgid "Test <0>URL</0>"
msgstr "Probar <0>URL</0>"
#: src/components/routes/settings/notifications.tsx:182
msgid "Test notification sent"
msgstr "Notificación de prueba enviada"
#: src/components/add-system.tsx:104
msgid "The agent must be running on the system to connect. Copy the installation command for the agent below."
msgstr "El agente debe estar ejecutándose en el sistema para conectarse. Copie el comando de instalación para el agente a continuación."
#: src/components/add-system.tsx:95
msgid "The agent must be running on the system to connect. Copy the<0>docker-compose.yml</0> for the agent below."
msgstr "El agente debe estar ejecutándose en el sistema para conectarse. Copie el <0>docker-compose.yml</0> para el agente a continuación."
#: src/components/login/forgot-pass-form.tsx:98
msgid "Then log into the backend and reset your user account password in the users table."
msgstr "Luego inicie sesión en el backend y restablezca la contraseña de su cuenta de usuario en la tabla de usuarios."
#: src/components/systems-table/systems-table.tsx:264
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
msgstr "Esta acción no se puede deshacer. Esto eliminará permanentemente todos los registros actuales de {name} de la base de datos."
#: src/components/routes/system.tsx:507
msgid "Throughput of {extraFsName}"
msgstr "Rendimiento de {extraFsName}"
#: src/components/routes/system.tsx:427
msgid "Throughput of root filesystem"
msgstr "Rendimiento del sistema de archivos raíz"
#: src/components/routes/settings/notifications.tsx:106
msgid "To email(s)"
msgstr "A correo(s)"
#: src/components/routes/system.tsx:350
#: src/components/routes/system.tsx:363
msgid "Toggle grid"
msgstr "Alternar cuadrícula"
#: src/components/mode-toggle.tsx:33
msgid "Toggle theme"
msgstr "Alternar tema"
#: src/lib/utils.ts:317
msgid "Triggers when any sensor exceeds a threshold"
msgstr "Se activa cuando cualquier sensor supera un umbral"
#: src/lib/utils.ts:310
msgid "Triggers when combined up/down exceeds a threshold"
msgstr "Se activa cuando la suma de subida/bajada supera un umbral"
#: src/lib/utils.ts:292
msgid "Triggers when CPU usage exceeds a threshold"
msgstr "Se activa cuando el uso de CPU supera un umbral"
#: src/lib/utils.ts:298
msgid "Triggers when memory usage exceeds a threshold"
msgstr "Se activa cuando el uso de memoria supera un umbral"
#: src/lib/utils.ts:285
msgid "Triggers when status switches between up and down"
msgstr "Se activa cuando el estado cambia entre activo e inactivo"
#: src/lib/utils.ts:304
msgid "Triggers when usage of any disk exceeds a threshold"
msgstr "Se activa cuando el uso de cualquier disco supera un umbral"
#: src/components/systems-table/systems-table.tsx:320
msgid "Updated in real time. Click on a system to view information."
msgstr "Actualizado en tiempo real. Haga clic en un sistema para ver la información."
#: src/components/routes/system.tsx:253
msgid "Uptime"
msgstr "Tiempo de actividad"
#: src/components/routes/system.tsx:494
msgid "Usage"
msgstr "Uso"
#: src/components/routes/system.tsx:415
msgid "Usage of root partition"
msgstr "Uso de la partición raíz"
#: src/components/charts/mem-chart.tsx:65
#: src/components/charts/swap-chart.tsx:56
msgid "Used"
msgstr "Usado"
#: src/components/login/auth-form.tsx:138
msgid "username"
msgstr "nombre de usuario"
#: src/components/login/auth-form.tsx:131
msgid "Username"
msgstr "Nombre de usuario"
#: src/components/command-palette.tsx:143
#: src/components/navbar.tsx:70
msgid "Users"
msgstr "Usuarios"
#: src/components/routes/system.tsx:603
msgid "Waiting for enough records to display"
msgstr "Esperando suficientes registros para mostrar"
#: src/components/routes/settings/general.tsx:48
msgid "Want to help us make our translations even better? Check out <0>Crowdin</0> for more details."
msgstr "¿Quieres ayudarnos a mejorar nuestras traducciones? Consulta <0>Crowdin</0> para más detalles."
#: src/components/routes/settings/notifications.tsx:124
msgid "Webhook / Push notifications"
msgstr "Notificaciones Webhook / Push"
#. Context is disk write
#: src/components/charts/area-chart.tsx:55
#: src/components/charts/area-chart.tsx:65
msgid "Write"
msgstr "Escritura"
#: src/components/routes/settings/layout.tsx:61
msgid "YAML Config"
msgstr "Configuración YAML"
#: src/components/routes/settings/config-yaml.tsx:45
msgid "YAML Configuration"
msgstr "Configuración YAML"
#: src/components/routes/settings/layout.tsx:34
msgid "Your user settings have been updated."
msgstr "Su configuración de usuario ha sido actualizada."

View File

@@ -0,0 +1,808 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2024-11-01 11:30-0400\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: fr\n"
"Project-Id-Version: beszel\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-11-04 20:46\n"
"Last-Translator: \n"
"Language-Team: French\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Crowdin-Project: beszel\n"
"X-Crowdin-Project-ID: 733311\n"
"X-Crowdin-Language: fr\n"
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 16\n"
#: src/components/routes/system.tsx:242
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# jour} other {# jours}}"
#: src/components/routes/system.tsx:240
msgid "{hours, plural, one {# hour} other {# hours}}"
msgstr "{hours, plural, one {# heure} other {# heures}}"
#: src/lib/utils.ts:139
msgid "1 hour"
msgstr "1 heure"
#: src/lib/utils.ts:162
msgid "1 week"
msgstr "1 semaine"
#: src/lib/utils.ts:147
msgid "12 hours"
msgstr "12 heures"
#: src/lib/utils.ts:155
msgid "24 hours"
msgstr "24 heures"
#: src/lib/utils.ts:170
msgid "30 days"
msgstr "30 jours"
#. Table column
#: src/components/systems-table/systems-table.tsx:207
msgid "Actions"
msgstr "Actions"
#: src/components/routes/home.tsx:62
msgid "Active Alerts"
msgstr "Alertes actives"
#: src/components/add-system.tsx:74
msgid "Add <0>System</0>"
msgstr "Ajouter <0>Système</0>"
#: src/components/add-system.tsx:83
msgid "Add New System"
msgstr "Ajouter un nouveau système"
#: src/components/add-system.tsx:167
#: src/components/add-system.tsx:178
msgid "Add system"
msgstr "Ajouter un système"
#: src/components/routes/settings/notifications.tsx:156
msgid "Add URL"
msgstr "Ajouter URL"
#: src/components/routes/settings/general.tsx:81
msgid "Adjust display options for charts."
msgstr "Ajuster les options d'affichage pour les graphiques."
#: src/components/command-palette.tsx:133
#: src/components/command-palette.tsx:146
#: src/components/command-palette.tsx:160
#: src/components/command-palette.tsx:174
#: src/components/command-palette.tsx:189
#: src/components/command-palette.tsx:204
msgid "Admin"
msgstr "Admin"
#: src/components/systems-table/systems-table.tsx:186
msgid "Agent"
msgstr "Agent"
#: src/components/alerts/alert-button.tsx:32
#: src/components/alerts/alert-button.tsx:68
msgid "Alerts"
msgstr "Alertes"
#: src/components/alerts/alert-button.tsx:88
#: src/components/systems-table/systems-table.tsx:317
msgid "All Systems"
msgstr "Tous les systèmes"
#: src/components/systems-table/systems-table.tsx:261
msgid "Are you sure you want to delete {name}?"
msgstr "Êtes-vous sûr de vouloir supprimer {name} ?"
#: src/components/command-palette.tsx:186
#: src/components/navbar.tsx:102
msgid "Auth Providers"
msgstr "Fournisseurs d'authentification"
#: src/components/copy-to-clipboard.tsx:16
msgid "Automatic copy requires a secure context."
msgstr "La copie automatique nécessite un contexte sécurisé."
#: src/components/routes/system.tsx:568
msgid "Average"
msgstr "Moyenne"
#: src/components/routes/system.tsx:387
msgid "Average CPU utilization of containers"
msgstr "Utilisation moyenne du CPU des conteneurs"
#: src/components/alerts/alerts-system.tsx:204
msgid "Average exceeds <0>{value}{0}</0>"
msgstr "La moyenne dépasse <0>{value}{0}</0>"
#: src/components/routes/system.tsx:376
msgid "Average system-wide CPU utilization"
msgstr "Utilisation moyenne du CPU à l'échelle du système"
#: src/components/command-palette.tsx:171
#: src/components/navbar.tsx:94
msgid "Backups"
msgstr "Sauvegardes"
#: src/components/routes/system.tsx:436
#: src/lib/utils.ts:307
msgid "Bandwidth"
msgstr "Bande passante"
#: src/components/login/auth-form.tsx:313
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
msgstr "Beszel prend en charge OpenID Connect et de nombreux fournisseurs d'authentification OAuth2."
#: src/components/routes/settings/notifications.tsx:127
msgid "Beszel uses <0>Shoutrrr</0> to integrate with popular notification services."
msgstr "Beszel utilise <0>Shoutrrr</0> pour s'intégrer aux services de notification populaires."
#: src/components/add-system.tsx:88
msgid "Binary"
msgstr "Binaire"
#: src/components/charts/mem-chart.tsx:89
msgid "Cache / Buffers"
msgstr "Cache / Tampons"
#: src/components/systems-table/systems-table.tsx:272
msgid "Cancel"
msgstr "Annuler"
#: src/components/routes/settings/config-yaml.tsx:68
msgid "Caution - potential data loss"
msgstr "Attention - perte de données potentielle"
#: src/components/routes/settings/general.tsx:36
msgid "Change general application options."
msgstr "Modifier les options générales de l'application."
#: src/components/routes/settings/general.tsx:78
msgid "Chart options"
msgstr "Options de graphique"
#: src/components/login/forgot-pass-form.tsx:34
msgid "Check {email} for a reset link."
msgstr "Vérifiez {email} pour un lien de réinitialisation."
#: src/components/routes/settings/layout.tsx:40
msgid "Check logs for more details."
msgstr "Vérifiez les journaux pour plus de détails."
#: src/components/routes/settings/notifications.tsx:183
msgid "Check your notification service"
msgstr "Vérifiez votre service de notification"
#: src/components/add-system.tsx:153
msgid "Click to copy"
msgstr "Cliquez pour copier"
#. Context: table columns
#: src/components/systems-table/systems-table.tsx:328
msgid "Columns"
msgstr "Colonnes"
#: src/components/login/forgot-pass-form.tsx:83
#: src/components/login/forgot-pass-form.tsx:89
msgid "Command line instructions"
msgstr "Instructions en ligne de commande"
#: src/components/routes/settings/notifications.tsx:77
msgid "Configure how you receive alert notifications."
msgstr "Configurez comment vous recevez les notifications d'alerte."
#: src/components/login/auth-form.tsx:189
#: src/components/login/auth-form.tsx:194
msgid "Confirm password"
msgstr "Confirmer le mot de passe"
#: src/components/systems-table/systems-table.tsx:278
msgid "Continue"
msgstr "Continuer"
#: src/lib/utils.ts:25
msgid "Copied to clipboard"
msgstr "Copié dans le presse-papiers"
#: src/components/add-system.tsx:164
msgid "Copy"
msgstr "Copier"
#: src/components/systems-table/systems-table.tsx:247
msgid "Copy host"
msgstr "Copier l'hôte"
#: src/components/add-system.tsx:175
msgid "Copy Linux command"
msgstr "Copier la commande Linux"
#: src/components/copy-to-clipboard.tsx:13
msgid "Copy text"
msgstr "Copier le texte"
#: src/components/systems-table/systems-table.tsx:152
msgid "CPU"
msgstr "CPU"
#: src/components/charts/area-chart.tsx:52
#: src/components/routes/system.tsx:375
#: src/lib/utils.ts:289
msgid "CPU Usage"
msgstr "Utilisation du CPU"
#: src/components/login/auth-form.tsx:215
msgid "Create account"
msgstr "Créer un compte"
#. Dark theme
#: src/components/mode-toggle.tsx:21
msgid "Dark"
msgstr "Sombre"
#: src/components/command-palette.tsx:82
#: src/components/routes/home.tsx:35
msgid "Dashboard"
msgstr "Tableau de bord"
#: src/components/routes/settings/general.tsx:85
msgid "Default time period"
msgstr "Période par défaut"
#: src/components/systems-table/systems-table.tsx:253
msgid "Delete"
msgstr "Supprimer"
#: src/components/systems-table/systems-table.tsx:166
msgid "Disk"
msgstr "Disque"
#: src/components/routes/system.tsx:426
msgid "Disk I/O"
msgstr "Entrée/Sortie disque"
#: src/components/charts/disk-chart.tsx:74
#: src/components/routes/system.tsx:415
#: src/lib/utils.ts:301
msgid "Disk Usage"
msgstr "Utilisation du disque"
#: src/components/routes/system.tsx:495
msgid "Disk usage of {extraFsName}"
msgstr "Utilisation du disque de {extraFsName}"
#: src/components/routes/system.tsx:386
msgid "Docker CPU Usage"
msgstr "Utilisation du CPU Docker"
#: src/components/routes/system.tsx:407
msgid "Docker Memory Usage"
msgstr "Utilisation de la mémoire Docker"
#: src/components/routes/system.tsx:452
msgid "Docker Network I/O"
msgstr "Entrée/Sortie réseau Docker"
#: src/components/command-palette.tsx:125
msgid "Documentation"
msgstr "Documentation"
#: src/components/login/auth-form.tsx:158
msgid "email"
msgstr "email"
#: src/components/login/auth-form.tsx:152
#: src/components/login/forgot-pass-form.tsx:53
msgid "Email"
msgstr "Email"
#: src/components/routes/settings/notifications.tsx:91
msgid "Email notifications"
msgstr "Notifications par email"
#: src/components/login/login.tsx:36
msgid "Enter email address to reset password"
msgstr "Entrez l'adresse email pour réinitialiser le mot de passe"
#: src/components/routes/settings/notifications.tsx:111
msgid "Enter email address..."
msgstr "Entrez l'adresse email..."
#: src/components/login/auth-form.tsx:256
#: src/components/routes/settings/config-yaml.tsx:28
#: src/components/routes/settings/notifications.tsx:187
msgid "Error"
msgstr "Erreur"
#: src/components/routes/home.tsx:81
msgid "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
msgstr "Dépasse {0}{1} dans la dernière {2, plural, one {# minute} other {# minutes}}"
#: src/components/routes/settings/config-yaml.tsx:72
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
msgstr "Les systèmes existants non définis dans <0>config.yml</0> seront supprimés. Veuillez faire des sauvegardes régulières."
#: src/components/routes/settings/config-yaml.tsx:93
msgid "Export configuration"
msgstr "Exporter la configuration"
#: src/components/routes/settings/config-yaml.tsx:48
msgid "Export your current systems configuration."
msgstr "Exportez la configuration actuelle de vos systèmes."
#: src/lib/utils.ts:38
msgid "Failed to authenticate"
msgstr "Échec de l'authentification"
#: src/components/routes/settings/layout.tsx:39
#: src/components/routes/settings/notifications.tsx:62
msgid "Failed to save settings"
msgstr "Échec de l'enregistrement des paramètres"
#: src/components/routes/settings/notifications.tsx:188
msgid "Failed to send test notification"
msgstr "Échec de l'envoi de la notification de test"
#: src/components/alerts/alerts-system.tsx:27
msgid "Failed to update alert"
msgstr "Échec de la mise à jour de l'alerte"
#: src/components/routes/system.tsx:539
#: src/components/systems-table/systems-table.tsx:324
msgid "Filter..."
msgstr "Filtrer..."
#: src/components/alerts/alerts-system.tsx:225
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
msgstr "Pour <0>{min}</0> {min, plural, one {minute} other {minutes}}"
#: src/components/login/auth-form.tsx:337
msgid "Forgot password?"
msgstr "Mot de passe oublié ?"
#. Context: General settings
#: src/components/routes/settings/general.tsx:33
#: src/components/routes/settings/layout.tsx:51
msgid "General"
msgstr "Général"
#: src/components/add-system.tsx:119
msgid "Host / IP"
msgstr "Hôte / IP"
#: src/components/login/forgot-pass-form.tsx:93
msgid "If you've lost the password to your admin account, you may reset it using the following command."
msgstr "Si vous avez perdu le mot de passe de votre compte administrateur, vous pouvez le réinitialiser en utilisant la commande suivante."
#: src/components/login/auth-form.tsx:16
msgid "Invalid email address."
msgstr "Adresse email invalide."
#. Linux kernel
#: src/components/routes/system.tsx:254
msgid "Kernel"
msgstr "Noyau"
#: src/components/routes/settings/general.tsx:45
msgid "Language"
msgstr "Langue"
#. Light theme
#: src/components/mode-toggle.tsx:16
msgid "Light"
msgstr "Clair"
#: src/components/navbar.tsx:113
msgid "Log Out"
msgstr "Déconnexion"
#: src/components/login/login.tsx:17
msgid "Login"
msgstr "Connexion"
#: src/components/login/auth-form.tsx:42
#: src/components/login/forgot-pass-form.tsx:15
msgid "Login attempt failed"
msgstr "Échec de la tentative de connexion"
#: src/components/command-palette.tsx:157
#: src/components/navbar.tsx:86
msgid "Logs"
msgstr "Journaux"
#: src/components/routes/settings/notifications.tsx:80
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
msgstr "Vous cherchez plutôt où créer des alertes ? Cliquez sur les icônes de cloche <0/> dans le tableau des systèmes."
#: src/components/routes/settings/layout.tsx:85
msgid "Manage display and notification preferences."
msgstr "Gérer les préférences d'affichage et de notification."
#. Chart select field. Please try to keep this short.
#: src/components/routes/system.tsx:571
msgid "Max 1 min"
msgstr "Max 1 min"
#: src/components/systems-table/systems-table.tsx:159
msgid "Memory"
msgstr "Mémoire"
#: src/components/routes/system.tsx:397
#: src/lib/utils.ts:295
msgid "Memory Usage"
msgstr "Utilisation de la mémoire"
#: src/components/routes/system.tsx:408
msgid "Memory usage of docker containers"
msgstr "Utilisation de la mémoire des conteneurs Docker"
#: src/components/add-system.tsx:113
msgid "Name"
msgstr "Nom"
#: src/components/systems-table/systems-table.tsx:173
msgid "Net"
msgstr "Net"
#: src/components/routes/system.tsx:453
msgid "Network traffic of docker containers"
msgstr "Trafic réseau des conteneurs Docker"
#: src/components/routes/system.tsx:438
msgid "Network traffic of public interfaces"
msgstr "Trafic réseau des interfaces publiques"
#: src/components/command-palette.tsx:50
msgid "No results found."
msgstr "Aucun résultat trouvé."
#: src/components/systems-table/systems-table.tsx:400
msgid "No systems found."
msgstr "Aucun système trouvé."
#: src/components/command-palette.tsx:111
#: src/components/routes/settings/layout.tsx:56
#: src/components/routes/settings/notifications.tsx:74
msgid "Notifications"
msgstr "Notifications"
#: src/components/login/auth-form.tsx:308
msgid "OAuth 2 / OIDC support"
msgstr "Support OAuth 2 / OIDC"
#: src/components/routes/settings/config-yaml.tsx:61
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
msgstr "À chaque redémarrage, les systèmes dans la base de données seront mis à jour pour correspondre aux systèmes définis dans le fichier."
#: src/components/systems-table/systems-table.tsx:219
msgid "Open menu"
msgstr "Ouvrir le menu"
#: src/components/login/auth-form.tsx:227
msgid "Or continue with"
msgstr "Ou continuer avec"
#: src/components/alerts/alert-button.tsx:109
msgid "Overwrite existing alerts"
msgstr "Écraser les alertes existantes"
#: src/components/command-palette.tsx:85
msgid "Page"
msgstr "Page"
#: src/components/command-palette.tsx:72
msgid "Pages / Settings"
msgstr "Pages / Paramètres"
#: src/components/login/auth-form.tsx:171
#: src/components/login/auth-form.tsx:176
msgid "Password"
msgstr "Mot de passe"
#: src/components/login/auth-form.tsx:17
msgid "Password must be at least 10 characters."
msgstr "Le mot de passe doit contenir au moins 10 caractères."
#: src/components/login/forgot-pass-form.tsx:33
msgid "Password reset request received"
msgstr "Demande de réinitialisation du mot de passe reçue"
#: src/components/systems-table/systems-table.tsx:241
msgid "Pause"
msgstr "Pause"
#: src/components/routes/settings/notifications.tsx:95
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
msgstr "Veuillez <0>configurer un serveur SMTP</0> pour garantir la livraison des alertes."
#: src/components/alerts/alerts-system.tsx:28
msgid "Please check logs for more details."
msgstr "Veuillez vérifier les journaux pour plus de détails."
#: src/components/login/auth-form.tsx:43
#: src/components/login/forgot-pass-form.tsx:16
msgid "Please check your credentials and try again"
msgstr "Veuillez vérifier vos identifiants et réessayer"
#: src/components/login/login.tsx:34
msgid "Please create an admin account"
msgstr "Veuillez créer un compte administrateur"
#: src/components/login/auth-form.tsx:257
msgid "Please enable pop-ups for this site"
msgstr "Veuillez activer les pop-ups pour ce site"
#: src/lib/utils.ts:39
msgid "Please log in again"
msgstr "Veuillez vous reconnecter"
#: src/components/login/auth-form.tsx:316
msgid "Please see <0>the documentation</0> for instructions."
msgstr "Veuillez consulter <0>la documentation</0> pour les instructions."
#: src/components/login/login.tsx:38
msgid "Please sign in to your account"
msgstr "Veuillez vous connecter à votre compte"
#: src/components/add-system.tsx:125
msgid "Port"
msgstr "Port"
#: src/components/routes/system.tsx:398
msgid "Precise utilization at the recorded time"
msgstr "Utilisation précise au moment enregistré"
#: src/components/routes/settings/general.tsx:58
msgid "Preferred Language"
msgstr "Langue préférée"
#. Use 'Key' if your language requires many more characters
#: src/components/add-system.tsx:131
msgid "Public Key"
msgstr "Clé publique"
#. Context is disk read
#: src/components/charts/area-chart.tsx:56
#: src/components/charts/area-chart.tsx:66
msgid "Read"
msgstr "Lecture"
#. Context is network bytes received (download)
#: src/components/charts/area-chart.tsx:61
msgid "Received"
msgstr "Reçu"
#: src/components/login/forgot-pass-form.tsx:76
msgid "Reset Password"
msgstr "Réinitialiser le mot de passe"
#: src/components/systems-table/systems-table.tsx:236
msgid "Resume"
msgstr "Reprendre"
#: src/components/routes/settings/notifications.tsx:117
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
msgstr "Enregistrez l'adresse en utilisant la touche Entrée ou la virgule. Laissez vide pour désactiver les notifications par email."
#: src/components/routes/settings/general.tsx:106
#: src/components/routes/settings/notifications.tsx:167
msgid "Save Settings"
msgstr "Enregistrer les paramètres"
#: src/components/navbar.tsx:142
msgid "Search"
msgstr "Recherche"
#: src/components/command-palette.tsx:47
msgid "Search for systems or settings..."
msgstr "Rechercher des systèmes ou des paramètres..."
#: src/components/alerts/alert-button.tsx:71
msgid "See <0>notification settings</0> to configure how you receive alerts."
msgstr "Voir les <0>paramètres de notification</0> pour configurer comment vous recevez les alertes."
#. Context is network bytes sent (upload)
#: src/components/charts/area-chart.tsx:60
msgid "Sent"
msgstr "Envoyé"
#: src/components/routes/settings/general.tsx:100
msgid "Sets the default time range for charts when a system is viewed."
msgstr "Définit la plage de temps par défaut pour les graphiques lorsqu'un système est consulté."
#: src/components/command-palette.tsx:96
#: src/components/command-palette.tsx:99
#: src/components/command-palette.tsx:114
#: src/components/routes/settings/layout.tsx:71
#: src/components/routes/settings/layout.tsx:82
msgid "Settings"
msgstr "Paramètres"
#: src/components/routes/settings/layout.tsx:33
msgid "Settings saved"
msgstr "Paramètres enregistrés"
#: src/components/login/auth-form.tsx:215
msgid "Sign in"
msgstr "Se connecter"
#: src/components/command-palette.tsx:201
msgid "SMTP settings"
msgstr "Paramètres SMTP"
#: src/lib/utils.ts:282
msgid "Status"
msgstr "Statut"
#: src/components/routes/system.tsx:467
msgid "Swap space used by the system"
msgstr "Espace d'échange utilisé par le système"
#: src/components/routes/system.tsx:466
msgid "Swap Usage"
msgstr "Utilisation de l'échange"
#. System theme
#: src/components/mode-toggle.tsx:26
#: src/components/systems-table/systems-table.tsx:110
#: src/components/systems-table/systems-table.tsx:121
msgid "System"
msgstr "Système"
#: src/components/navbar.tsx:78
msgid "Systems"
msgstr "Systèmes"
#: src/components/routes/settings/config-yaml.tsx:55
msgid "Systems may be managed in a <0>config.yml</0> file inside your data directory."
msgstr "Les systèmes peuvent être gérés dans un fichier <0>config.yml</0> à l'intérieur de votre répertoire de données."
#: src/components/routes/system.tsx:477
#: src/lib/utils.ts:314
msgid "Temperature"
msgstr "Température"
#: src/components/routes/system.tsx:478
msgid "Temperatures of system sensors"
msgstr "Températures des capteurs du système"
#: src/components/routes/settings/notifications.tsx:211
msgid "Test <0>URL</0>"
msgstr "Tester <0>URL</0>"
#: src/components/routes/settings/notifications.tsx:182
msgid "Test notification sent"
msgstr "Notification de test envoyée"
#: src/components/add-system.tsx:104
msgid "The agent must be running on the system to connect. Copy the installation command for the agent below."
msgstr "L'agent doit être en cours d'exécution sur le système pour se connecter. Copiez la commande d'installation pour l'agent ci-dessous."
#: src/components/add-system.tsx:95
msgid "The agent must be running on the system to connect. Copy the<0>docker-compose.yml</0> for the agent below."
msgstr "L'agent doit être en cours d'exécution sur le système pour se connecter. Copiez le <0>docker-compose.yml</0> pour l'agent ci-dessous."
#: src/components/login/forgot-pass-form.tsx:98
msgid "Then log into the backend and reset your user account password in the users table."
msgstr "Ensuite, connectez-vous au backend et réinitialisez le mot de passe de votre compte utilisateur dans la table des utilisateurs."
#: src/components/systems-table/systems-table.tsx:264
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
msgstr "Cette action ne peut pas être annulée. Cela supprimera définitivement tous les enregistrements actuels pour {name} de la base de données."
#: src/components/routes/system.tsx:507
msgid "Throughput of {extraFsName}"
msgstr "Débit de {extraFsName}"
#: src/components/routes/system.tsx:427
msgid "Throughput of root filesystem"
msgstr "Débit du système de fichiers racine"
#: src/components/routes/settings/notifications.tsx:106
msgid "To email(s)"
msgstr "Aux email(s)"
#: src/components/routes/system.tsx:350
#: src/components/routes/system.tsx:363
msgid "Toggle grid"
msgstr "Basculer la grille"
#: src/components/mode-toggle.tsx:33
msgid "Toggle theme"
msgstr "Basculer le thème"
#: src/lib/utils.ts:317
msgid "Triggers when any sensor exceeds a threshold"
msgstr "Déclenchement lorsque tout capteur dépasse un seuil"
#: src/lib/utils.ts:310
msgid "Triggers when combined up/down exceeds a threshold"
msgstr "Déclenchement lorsque la montée/descente combinée dépasse un seuil"
#: src/lib/utils.ts:292
msgid "Triggers when CPU usage exceeds a threshold"
msgstr "Déclenchement lorsque l'utilisation du CPU dépasse un seuil"
#: src/lib/utils.ts:298
msgid "Triggers when memory usage exceeds a threshold"
msgstr "Déclenchement lorsque l'utilisation de la mémoire dépasse un seuil"
#: src/lib/utils.ts:285
msgid "Triggers when status switches between up and down"
msgstr "Déclenchement lorsque le statut passe de haut en bas"
#: src/lib/utils.ts:304
msgid "Triggers when usage of any disk exceeds a threshold"
msgstr "Déclenchement lorsque l'utilisation de tout disque dépasse un seuil"
#: src/components/systems-table/systems-table.tsx:320
msgid "Updated in real time. Click on a system to view information."
msgstr "Mis à jour en temps réel. Cliquez sur un système pour voir les informations."
#: src/components/routes/system.tsx:253
msgid "Uptime"
msgstr "Temps de fonctionnement"
#: src/components/routes/system.tsx:494
msgid "Usage"
msgstr "Utilisation"
#: src/components/routes/system.tsx:415
msgid "Usage of root partition"
msgstr "Utilisation de la partition racine"
#: src/components/charts/mem-chart.tsx:65
#: src/components/charts/swap-chart.tsx:56
msgid "Used"
msgstr "Utilisé"
#: src/components/login/auth-form.tsx:138
msgid "username"
msgstr "nom d'utilisateur"
#: src/components/login/auth-form.tsx:131
msgid "Username"
msgstr "Nom d'utilisateur"
#: src/components/command-palette.tsx:143
#: src/components/navbar.tsx:70
msgid "Users"
msgstr "Utilisateurs"
#: src/components/routes/system.tsx:603
msgid "Waiting for enough records to display"
msgstr "En attente de suffisamment d'enregistrements à afficher"
#: src/components/routes/settings/general.tsx:48
msgid "Want to help us make our translations even better? Check out <0>Crowdin</0> for more details."
msgstr "Vous voulez nous aider à améliorer nos traductions ? Consultez <0>Crowdin</0> pour plus de détails."
#: src/components/routes/settings/notifications.tsx:124
msgid "Webhook / Push notifications"
msgstr "Notifications Webhook / Push"
#. Context is disk write
#: src/components/charts/area-chart.tsx:55
#: src/components/charts/area-chart.tsx:65
msgid "Write"
msgstr "Écriture"
#: src/components/routes/settings/layout.tsx:61
msgid "YAML Config"
msgstr "Configuration YAML"
#: src/components/routes/settings/config-yaml.tsx:45
msgid "YAML Configuration"
msgstr "Configuration YAML"
#: src/components/routes/settings/layout.tsx:34
msgid "Your user settings have been updated."
msgstr "Vos paramètres utilisateur ont été mis à jour."

View File

@@ -0,0 +1,808 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2024-11-01 11:30-0400\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: it\n"
"Project-Id-Version: beszel\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-11-04 20:47\n"
"Last-Translator: \n"
"Language-Team: Italian\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: beszel\n"
"X-Crowdin-Project-ID: 733311\n"
"X-Crowdin-Language: it\n"
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 16\n"
#: src/components/routes/system.tsx:242
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# giorno} other {# giorni}}"
#: src/components/routes/system.tsx:240
msgid "{hours, plural, one {# hour} other {# hours}}"
msgstr "{hours, plural, one {# ora} other {# ore}}"
#: src/lib/utils.ts:139
msgid "1 hour"
msgstr "1 ora"
#: src/lib/utils.ts:162
msgid "1 week"
msgstr "1 settimana"
#: src/lib/utils.ts:147
msgid "12 hours"
msgstr "12 ore"
#: src/lib/utils.ts:155
msgid "24 hours"
msgstr "24 ore"
#: src/lib/utils.ts:170
msgid "30 days"
msgstr "30 giorni"
#. Table column
#: src/components/systems-table/systems-table.tsx:207
msgid "Actions"
msgstr "Azioni"
#: src/components/routes/home.tsx:62
msgid "Active Alerts"
msgstr "Avvisi Attivi"
#: src/components/add-system.tsx:74
msgid "Add <0>System</0>"
msgstr "Aggiungi <0>Sistema</0>"
#: src/components/add-system.tsx:83
msgid "Add New System"
msgstr "Aggiungi Nuovo Sistema"
#: src/components/add-system.tsx:167
#: src/components/add-system.tsx:178
msgid "Add system"
msgstr "Aggiungi sistema"
#: src/components/routes/settings/notifications.tsx:156
msgid "Add URL"
msgstr "Aggiungi URL"
#: src/components/routes/settings/general.tsx:81
msgid "Adjust display options for charts."
msgstr "Regola le opzioni di visualizzazione per i grafici."
#: src/components/command-palette.tsx:133
#: src/components/command-palette.tsx:146
#: src/components/command-palette.tsx:160
#: src/components/command-palette.tsx:174
#: src/components/command-palette.tsx:189
#: src/components/command-palette.tsx:204
msgid "Admin"
msgstr "Amministratore"
#: src/components/systems-table/systems-table.tsx:186
msgid "Agent"
msgstr "Agente"
#: src/components/alerts/alert-button.tsx:32
#: src/components/alerts/alert-button.tsx:68
msgid "Alerts"
msgstr "Avvisi"
#: src/components/alerts/alert-button.tsx:88
#: src/components/systems-table/systems-table.tsx:317
msgid "All Systems"
msgstr "Tutti i Sistemi"
#: src/components/systems-table/systems-table.tsx:261
msgid "Are you sure you want to delete {name}?"
msgstr "Sei sicuro di voler eliminare {name}?"
#: src/components/command-palette.tsx:186
#: src/components/navbar.tsx:102
msgid "Auth Providers"
msgstr "Provider di Autenticazione"
#: src/components/copy-to-clipboard.tsx:16
msgid "Automatic copy requires a secure context."
msgstr "La copia automatica richiede un contesto sicuro."
#: src/components/routes/system.tsx:568
msgid "Average"
msgstr "Media"
#: src/components/routes/system.tsx:387
msgid "Average CPU utilization of containers"
msgstr "Utilizzo medio della CPU dei container"
#: src/components/alerts/alerts-system.tsx:204
msgid "Average exceeds <0>{value}{0}</0>"
msgstr "La media supera <0>{value}{0}</0>"
#: src/components/routes/system.tsx:376
msgid "Average system-wide CPU utilization"
msgstr "Utilizzo medio della CPU a livello di sistema"
#: src/components/command-palette.tsx:171
#: src/components/navbar.tsx:94
msgid "Backups"
msgstr "Backup"
#: src/components/routes/system.tsx:436
#: src/lib/utils.ts:307
msgid "Bandwidth"
msgstr "Larghezza di banda"
#: src/components/login/auth-form.tsx:313
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
msgstr "Beszel supporta OpenID Connect e molti provider di autenticazione OAuth2."
#: src/components/routes/settings/notifications.tsx:127
msgid "Beszel uses <0>Shoutrrr</0> to integrate with popular notification services."
msgstr "Beszel utilizza <0>Shoutrrr</0> per integrarsi con i servizi di notifica popolari."
#: src/components/add-system.tsx:88
msgid "Binary"
msgstr "Binario"
#: src/components/charts/mem-chart.tsx:89
msgid "Cache / Buffers"
msgstr "Cache / Buffer"
#: src/components/systems-table/systems-table.tsx:272
msgid "Cancel"
msgstr "Annulla"
#: src/components/routes/settings/config-yaml.tsx:68
msgid "Caution - potential data loss"
msgstr "Attenzione - possibile perdita di dati"
#: src/components/routes/settings/general.tsx:36
msgid "Change general application options."
msgstr "Modifica le opzioni generali dell'applicazione."
#: src/components/routes/settings/general.tsx:78
msgid "Chart options"
msgstr "Opzioni del grafico"
#: src/components/login/forgot-pass-form.tsx:34
msgid "Check {email} for a reset link."
msgstr "Controlla {email} per un link di reset."
#: src/components/routes/settings/layout.tsx:40
msgid "Check logs for more details."
msgstr "Controlla i log per maggiori dettagli."
#: src/components/routes/settings/notifications.tsx:183
msgid "Check your notification service"
msgstr "Controlla il tuo servizio di notifica"
#: src/components/add-system.tsx:153
msgid "Click to copy"
msgstr "Clicca per copiare"
#. Context: table columns
#: src/components/systems-table/systems-table.tsx:328
msgid "Columns"
msgstr "Colonne"
#: src/components/login/forgot-pass-form.tsx:83
#: src/components/login/forgot-pass-form.tsx:89
msgid "Command line instructions"
msgstr "Istruzioni da riga di comando"
#: src/components/routes/settings/notifications.tsx:77
msgid "Configure how you receive alert notifications."
msgstr "Configura come ricevere le notifiche di avviso."
#: src/components/login/auth-form.tsx:189
#: src/components/login/auth-form.tsx:194
msgid "Confirm password"
msgstr "Conferma password"
#: src/components/systems-table/systems-table.tsx:278
msgid "Continue"
msgstr "Continua"
#: src/lib/utils.ts:25
msgid "Copied to clipboard"
msgstr "Copiato negli appunti"
#: src/components/add-system.tsx:164
msgid "Copy"
msgstr "Copia"
#: src/components/systems-table/systems-table.tsx:247
msgid "Copy host"
msgstr "Copia host"
#: src/components/add-system.tsx:175
msgid "Copy Linux command"
msgstr "Copia comando Linux"
#: src/components/copy-to-clipboard.tsx:13
msgid "Copy text"
msgstr "Copia testo"
#: src/components/systems-table/systems-table.tsx:152
msgid "CPU"
msgstr "CPU"
#: src/components/charts/area-chart.tsx:52
#: src/components/routes/system.tsx:375
#: src/lib/utils.ts:289
msgid "CPU Usage"
msgstr "Utilizzo CPU"
#: src/components/login/auth-form.tsx:215
msgid "Create account"
msgstr "Crea account"
#. Dark theme
#: src/components/mode-toggle.tsx:21
msgid "Dark"
msgstr "Scuro"
#: src/components/command-palette.tsx:82
#: src/components/routes/home.tsx:35
msgid "Dashboard"
msgstr "Cruscotto"
#: src/components/routes/settings/general.tsx:85
msgid "Default time period"
msgstr "Periodo di tempo predefinito"
#: src/components/systems-table/systems-table.tsx:253
msgid "Delete"
msgstr "Elimina"
#: src/components/systems-table/systems-table.tsx:166
msgid "Disk"
msgstr "Disco"
#: src/components/routes/system.tsx:426
msgid "Disk I/O"
msgstr "I/O Disco"
#: src/components/charts/disk-chart.tsx:74
#: src/components/routes/system.tsx:415
#: src/lib/utils.ts:301
msgid "Disk Usage"
msgstr "Utilizzo Disco"
#: src/components/routes/system.tsx:495
msgid "Disk usage of {extraFsName}"
msgstr "Utilizzo del disco di {extraFsName}"
#: src/components/routes/system.tsx:386
msgid "Docker CPU Usage"
msgstr "Utilizzo CPU Docker"
#: src/components/routes/system.tsx:407
msgid "Docker Memory Usage"
msgstr "Utilizzo Memoria Docker"
#: src/components/routes/system.tsx:452
msgid "Docker Network I/O"
msgstr "I/O di Rete Docker"
#: src/components/command-palette.tsx:125
msgid "Documentation"
msgstr "Documentazione"
#: src/components/login/auth-form.tsx:158
msgid "email"
msgstr "email"
#: src/components/login/auth-form.tsx:152
#: src/components/login/forgot-pass-form.tsx:53
msgid "Email"
msgstr "Email"
#: src/components/routes/settings/notifications.tsx:91
msgid "Email notifications"
msgstr "Notifiche email"
#: src/components/login/login.tsx:36
msgid "Enter email address to reset password"
msgstr "Inserisci l'indirizzo email per reimpostare la password"
#: src/components/routes/settings/notifications.tsx:111
msgid "Enter email address..."
msgstr "Inserisci l'indirizzo email..."
#: src/components/login/auth-form.tsx:256
#: src/components/routes/settings/config-yaml.tsx:28
#: src/components/routes/settings/notifications.tsx:187
msgid "Error"
msgstr "Errore"
#: src/components/routes/home.tsx:81
msgid "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
msgstr "Supera {0}{1} negli ultimi {2, plural, one {# minuto} other {# minuti}}"
#: src/components/routes/settings/config-yaml.tsx:72
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
msgstr "I sistemi esistenti non definiti in <0>config.yml</0> verranno eliminati. Si prega di effettuare backup regolari."
#: src/components/routes/settings/config-yaml.tsx:93
msgid "Export configuration"
msgstr "Esporta configurazione"
#: src/components/routes/settings/config-yaml.tsx:48
msgid "Export your current systems configuration."
msgstr "Esporta la configurazione attuale dei tuoi sistemi."
#: src/lib/utils.ts:38
msgid "Failed to authenticate"
msgstr "Autenticazione fallita"
#: src/components/routes/settings/layout.tsx:39
#: src/components/routes/settings/notifications.tsx:62
msgid "Failed to save settings"
msgstr "Salvataggio delle impostazioni fallito"
#: src/components/routes/settings/notifications.tsx:188
msgid "Failed to send test notification"
msgstr "Invio della notifica di test fallito"
#: src/components/alerts/alerts-system.tsx:27
msgid "Failed to update alert"
msgstr "Aggiornamento dell'avviso fallito"
#: src/components/routes/system.tsx:539
#: src/components/systems-table/systems-table.tsx:324
msgid "Filter..."
msgstr "Filtra..."
#: src/components/alerts/alerts-system.tsx:225
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
msgstr "Per <0>{min}</0> {min, plural, one {minuto} other {minuti}}"
#: src/components/login/auth-form.tsx:337
msgid "Forgot password?"
msgstr "Password dimenticata?"
#. Context: General settings
#: src/components/routes/settings/general.tsx:33
#: src/components/routes/settings/layout.tsx:51
msgid "General"
msgstr "Generale"
#: src/components/add-system.tsx:119
msgid "Host / IP"
msgstr "Host / IP"
#: src/components/login/forgot-pass-form.tsx:93
msgid "If you've lost the password to your admin account, you may reset it using the following command."
msgstr "Se hai perso la password del tuo account amministratore, puoi reimpostarla utilizzando il seguente comando."
#: src/components/login/auth-form.tsx:16
msgid "Invalid email address."
msgstr "Indirizzo email non valido."
#. Linux kernel
#: src/components/routes/system.tsx:254
msgid "Kernel"
msgstr "Kernel"
#: src/components/routes/settings/general.tsx:45
msgid "Language"
msgstr "Lingua"
#. Light theme
#: src/components/mode-toggle.tsx:16
msgid "Light"
msgstr "Chiaro"
#: src/components/navbar.tsx:113
msgid "Log Out"
msgstr "Disconnetti"
#: src/components/login/login.tsx:17
msgid "Login"
msgstr "Accedi"
#: src/components/login/auth-form.tsx:42
#: src/components/login/forgot-pass-form.tsx:15
msgid "Login attempt failed"
msgstr "Tentativo di accesso fallito"
#: src/components/command-palette.tsx:157
#: src/components/navbar.tsx:86
msgid "Logs"
msgstr "Log"
#: src/components/routes/settings/notifications.tsx:80
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
msgstr "Cerchi invece dove creare avvisi? Clicca sulle icone della campana <0/> nella tabella dei sistemi."
#: src/components/routes/settings/layout.tsx:85
msgid "Manage display and notification preferences."
msgstr "Gestisci le preferenze di visualizzazione e notifica."
#. Chart select field. Please try to keep this short.
#: src/components/routes/system.tsx:571
msgid "Max 1 min"
msgstr "Max 1 min"
#: src/components/systems-table/systems-table.tsx:159
msgid "Memory"
msgstr "Memoria"
#: src/components/routes/system.tsx:397
#: src/lib/utils.ts:295
msgid "Memory Usage"
msgstr "Utilizzo Memoria"
#: src/components/routes/system.tsx:408
msgid "Memory usage of docker containers"
msgstr "Utilizzo della memoria dei container Docker"
#: src/components/add-system.tsx:113
msgid "Name"
msgstr "Nome"
#: src/components/systems-table/systems-table.tsx:173
msgid "Net"
msgstr "Rete"
#: src/components/routes/system.tsx:453
msgid "Network traffic of docker containers"
msgstr "Traffico di rete dei container Docker"
#: src/components/routes/system.tsx:438
msgid "Network traffic of public interfaces"
msgstr "Traffico di rete delle interfacce pubbliche"
#: src/components/command-palette.tsx:50
msgid "No results found."
msgstr "Nessun risultato trovato."
#: src/components/systems-table/systems-table.tsx:400
msgid "No systems found."
msgstr "Nessun sistema trovato."
#: src/components/command-palette.tsx:111
#: src/components/routes/settings/layout.tsx:56
#: src/components/routes/settings/notifications.tsx:74
msgid "Notifications"
msgstr "Notifiche"
#: src/components/login/auth-form.tsx:308
msgid "OAuth 2 / OIDC support"
msgstr "Supporto OAuth 2 / OIDC"
#: src/components/routes/settings/config-yaml.tsx:61
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
msgstr "Ad ogni riavvio, i sistemi nel database verranno aggiornati per corrispondere ai sistemi definiti nel file."
#: src/components/systems-table/systems-table.tsx:219
msgid "Open menu"
msgstr "Apri menu"
#: src/components/login/auth-form.tsx:227
msgid "Or continue with"
msgstr "Oppure continua con"
#: src/components/alerts/alert-button.tsx:109
msgid "Overwrite existing alerts"
msgstr "Sovrascrivi avvisi esistenti"
#: src/components/command-palette.tsx:85
msgid "Page"
msgstr "Pagina"
#: src/components/command-palette.tsx:72
msgid "Pages / Settings"
msgstr "Pagine / Impostazioni"
#: src/components/login/auth-form.tsx:171
#: src/components/login/auth-form.tsx:176
msgid "Password"
msgstr "Password"
#: src/components/login/auth-form.tsx:17
msgid "Password must be at least 10 characters."
msgstr "La password deve essere di almeno 10 caratteri."
#: src/components/login/forgot-pass-form.tsx:33
msgid "Password reset request received"
msgstr "Richiesta di reimpostazione password ricevuta"
#: src/components/systems-table/systems-table.tsx:241
msgid "Pause"
msgstr "Pausa"
#: src/components/routes/settings/notifications.tsx:95
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
msgstr "Si prega di <0>configurare un server SMTP</0> per garantire la consegna degli avvisi."
#: src/components/alerts/alerts-system.tsx:28
msgid "Please check logs for more details."
msgstr "Si prega di controllare i log per maggiori dettagli."
#: src/components/login/auth-form.tsx:43
#: src/components/login/forgot-pass-form.tsx:16
msgid "Please check your credentials and try again"
msgstr "Si prega di controllare le credenziali e riprovare"
#: src/components/login/login.tsx:34
msgid "Please create an admin account"
msgstr "Si prega di creare un account amministratore"
#: src/components/login/auth-form.tsx:257
msgid "Please enable pop-ups for this site"
msgstr "Si prega di abilitare i pop-up per questo sito"
#: src/lib/utils.ts:39
msgid "Please log in again"
msgstr "Si prega di accedere nuovamente"
#: src/components/login/auth-form.tsx:316
msgid "Please see <0>the documentation</0> for instructions."
msgstr "Si prega di consultare <0>la documentazione</0> per le istruzioni."
#: src/components/login/login.tsx:38
msgid "Please sign in to your account"
msgstr "Si prega di accedere al proprio account"
#: src/components/add-system.tsx:125
msgid "Port"
msgstr "Porta"
#: src/components/routes/system.tsx:398
msgid "Precise utilization at the recorded time"
msgstr "Utilizzo preciso al momento registrato"
#: src/components/routes/settings/general.tsx:58
msgid "Preferred Language"
msgstr "Lingua Preferita"
#. Use 'Key' if your language requires many more characters
#: src/components/add-system.tsx:131
msgid "Public Key"
msgstr "Chiave Pub"
#. Context is disk read
#: src/components/charts/area-chart.tsx:56
#: src/components/charts/area-chart.tsx:66
msgid "Read"
msgstr "Lettura"
#. Context is network bytes received (download)
#: src/components/charts/area-chart.tsx:61
msgid "Received"
msgstr "Ricevuto"
#: src/components/login/forgot-pass-form.tsx:76
msgid "Reset Password"
msgstr "Reimposta Password"
#: src/components/systems-table/systems-table.tsx:236
msgid "Resume"
msgstr "Riprendi"
#: src/components/routes/settings/notifications.tsx:117
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
msgstr "Salva l'indirizzo usando il tasto invio o la virgola. Lascia vuoto per disabilitare le notifiche email."
#: src/components/routes/settings/general.tsx:106
#: src/components/routes/settings/notifications.tsx:167
msgid "Save Settings"
msgstr "Salva Impostazioni"
#: src/components/navbar.tsx:142
msgid "Search"
msgstr "Cerca"
#: src/components/command-palette.tsx:47
msgid "Search for systems or settings..."
msgstr "Cerca sistemi o impostazioni..."
#: src/components/alerts/alert-button.tsx:71
msgid "See <0>notification settings</0> to configure how you receive alerts."
msgstr "Vedi <0>impostazioni di notifica</0> per configurare come ricevere gli avvisi."
#. Context is network bytes sent (upload)
#: src/components/charts/area-chart.tsx:60
msgid "Sent"
msgstr "Inviato"
#: src/components/routes/settings/general.tsx:100
msgid "Sets the default time range for charts when a system is viewed."
msgstr "Imposta l'intervallo di tempo predefinito per i grafici quando viene visualizzato un sistema."
#: src/components/command-palette.tsx:96
#: src/components/command-palette.tsx:99
#: src/components/command-palette.tsx:114
#: src/components/routes/settings/layout.tsx:71
#: src/components/routes/settings/layout.tsx:82
msgid "Settings"
msgstr "Impostazioni"
#: src/components/routes/settings/layout.tsx:33
msgid "Settings saved"
msgstr "Impostazioni salvate"
#: src/components/login/auth-form.tsx:215
msgid "Sign in"
msgstr "Accedi"
#: src/components/command-palette.tsx:201
msgid "SMTP settings"
msgstr "Impostazioni SMTP"
#: src/lib/utils.ts:282
msgid "Status"
msgstr "Stato"
#: src/components/routes/system.tsx:467
msgid "Swap space used by the system"
msgstr "Spazio di swap utilizzato dal sistema"
#: src/components/routes/system.tsx:466
msgid "Swap Usage"
msgstr "Utilizzo Swap"
#. System theme
#: src/components/mode-toggle.tsx:26
#: src/components/systems-table/systems-table.tsx:110
#: src/components/systems-table/systems-table.tsx:121
msgid "System"
msgstr "Sistema"
#: src/components/navbar.tsx:78
msgid "Systems"
msgstr "Sistemi"
#: src/components/routes/settings/config-yaml.tsx:55
msgid "Systems may be managed in a <0>config.yml</0> file inside your data directory."
msgstr "I sistemi possono essere gestiti in un file <0>config.yml</0> all'interno della tua directory dati."
#: src/components/routes/system.tsx:477
#: src/lib/utils.ts:314
msgid "Temperature"
msgstr "Temperatura"
#: src/components/routes/system.tsx:478
msgid "Temperatures of system sensors"
msgstr "Temperature dei sensori di sistema"
#: src/components/routes/settings/notifications.tsx:211
msgid "Test <0>URL</0>"
msgstr "Test <0>URL</0>"
#: src/components/routes/settings/notifications.tsx:182
msgid "Test notification sent"
msgstr "Notifica di test inviata"
#: src/components/add-system.tsx:104
msgid "The agent must be running on the system to connect. Copy the installation command for the agent below."
msgstr "L'agente deve essere in esecuzione sul sistema per connettersi. Copia il comando di installazione per l'agente qui sotto."
#: src/components/add-system.tsx:95
msgid "The agent must be running on the system to connect. Copy the<0>docker-compose.yml</0> for the agent below."
msgstr "L'agente deve essere in esecuzione sul sistema per connettersi. Copia il<0>docker-compose.yml</0> per l'agente qui sotto."
#: src/components/login/forgot-pass-form.tsx:98
msgid "Then log into the backend and reset your user account password in the users table."
msgstr "Quindi accedi al backend e reimposta la password del tuo account utente nella tabella degli utenti."
#: src/components/systems-table/systems-table.tsx:264
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
msgstr "Questa azione non può essere annullata. Questo eliminerà permanentemente tutti i record attuali per {name} dal database."
#: src/components/routes/system.tsx:507
msgid "Throughput of {extraFsName}"
msgstr "Throughput di {extraFsName}"
#: src/components/routes/system.tsx:427
msgid "Throughput of root filesystem"
msgstr "Throughput del filesystem root"
#: src/components/routes/settings/notifications.tsx:106
msgid "To email(s)"
msgstr "A email(s)"
#: src/components/routes/system.tsx:350
#: src/components/routes/system.tsx:363
msgid "Toggle grid"
msgstr "Attiva/disattiva griglia"
#: src/components/mode-toggle.tsx:33
msgid "Toggle theme"
msgstr "Attiva/disattiva tema"
#: src/lib/utils.ts:317
msgid "Triggers when any sensor exceeds a threshold"
msgstr "Attiva quando un sensore supera una soglia"
#: src/lib/utils.ts:310
msgid "Triggers when combined up/down exceeds a threshold"
msgstr "Attiva quando il combinato up/down supera una soglia"
#: src/lib/utils.ts:292
msgid "Triggers when CPU usage exceeds a threshold"
msgstr "Attiva quando l'utilizzo della CPU supera una soglia"
#: src/lib/utils.ts:298
msgid "Triggers when memory usage exceeds a threshold"
msgstr "Attiva quando l'utilizzo della memoria supera una soglia"
#: src/lib/utils.ts:285
msgid "Triggers when status switches between up and down"
msgstr "Attiva quando lo stato passa tra up e down"
#: src/lib/utils.ts:304
msgid "Triggers when usage of any disk exceeds a threshold"
msgstr "Attiva quando l'utilizzo di un disco supera una soglia"
#: src/components/systems-table/systems-table.tsx:320
msgid "Updated in real time. Click on a system to view information."
msgstr "Aggiornato in tempo reale. Clicca su un sistema per visualizzare le informazioni."
#: src/components/routes/system.tsx:253
msgid "Uptime"
msgstr "Tempo di attività"
#: src/components/routes/system.tsx:494
msgid "Usage"
msgstr "Utilizzo"
#: src/components/routes/system.tsx:415
msgid "Usage of root partition"
msgstr "Utilizzo della partizione root"
#: src/components/charts/mem-chart.tsx:65
#: src/components/charts/swap-chart.tsx:56
msgid "Used"
msgstr "Utilizzato"
#: src/components/login/auth-form.tsx:138
msgid "username"
msgstr "nome utente"
#: src/components/login/auth-form.tsx:131
msgid "Username"
msgstr "Nome utente"
#: src/components/command-palette.tsx:143
#: src/components/navbar.tsx:70
msgid "Users"
msgstr "Utenti"
#: src/components/routes/system.tsx:603
msgid "Waiting for enough records to display"
msgstr "In attesa di abbastanza record da visualizzare"
#: src/components/routes/settings/general.tsx:48
msgid "Want to help us make our translations even better? Check out <0>Crowdin</0> for more details."
msgstr "Vuoi aiutarci a migliorare ulteriormente le nostre traduzioni? Dai un'occhiata a <0>Crowdin</0> per maggiori dettagli."
#: src/components/routes/settings/notifications.tsx:124
msgid "Webhook / Push notifications"
msgstr "Notifiche Webhook / Push"
#. Context is disk write
#: src/components/charts/area-chart.tsx:55
#: src/components/charts/area-chart.tsx:65
msgid "Write"
msgstr "Scrittura"
#: src/components/routes/settings/layout.tsx:61
msgid "YAML Config"
msgstr "Configurazione YAML"
#: src/components/routes/settings/config-yaml.tsx:45
msgid "YAML Configuration"
msgstr "Configurazione YAML"
#: src/components/routes/settings/layout.tsx:34
msgid "Your user settings have been updated."
msgstr "Le impostazioni utente sono state aggiornate."

View File

@@ -0,0 +1,808 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2024-11-01 11:30-0400\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: ja\n"
"Project-Id-Version: beszel\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-11-04 20:46\n"
"Last-Translator: \n"
"Language-Team: Japanese\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Crowdin-Project: beszel\n"
"X-Crowdin-Project-ID: 733311\n"
"X-Crowdin-Language: ja\n"
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 16\n"
#: src/components/routes/system.tsx:242
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# 日} other {# 日}}"
#: src/components/routes/system.tsx:240
msgid "{hours, plural, one {# hour} other {# hours}}"
msgstr "{hours, plural, one {# 時間} other {# 時間}}"
#: src/lib/utils.ts:139
msgid "1 hour"
msgstr "1時間"
#: src/lib/utils.ts:162
msgid "1 week"
msgstr "1週間"
#: src/lib/utils.ts:147
msgid "12 hours"
msgstr "12時間"
#: src/lib/utils.ts:155
msgid "24 hours"
msgstr "24時間"
#: src/lib/utils.ts:170
msgid "30 days"
msgstr "30日間"
#. Table column
#: src/components/systems-table/systems-table.tsx:207
msgid "Actions"
msgstr "アクション"
#: src/components/routes/home.tsx:62
msgid "Active Alerts"
msgstr "アクティブなアラート"
#: src/components/add-system.tsx:74
msgid "Add <0>System</0>"
msgstr "<0>システム</0>を追加"
#: src/components/add-system.tsx:83
msgid "Add New System"
msgstr "新しいシステムを追加"
#: src/components/add-system.tsx:167
#: src/components/add-system.tsx:178
msgid "Add system"
msgstr "システムを追加"
#: src/components/routes/settings/notifications.tsx:156
msgid "Add URL"
msgstr "URLを追加"
#: src/components/routes/settings/general.tsx:81
msgid "Adjust display options for charts."
msgstr "チャートの表示オプションを調整します。"
#: src/components/command-palette.tsx:133
#: src/components/command-palette.tsx:146
#: src/components/command-palette.tsx:160
#: src/components/command-palette.tsx:174
#: src/components/command-palette.tsx:189
#: src/components/command-palette.tsx:204
msgid "Admin"
msgstr "管理者"
#: src/components/systems-table/systems-table.tsx:186
msgid "Agent"
msgstr "代理"
#: src/components/alerts/alert-button.tsx:32
#: src/components/alerts/alert-button.tsx:68
msgid "Alerts"
msgstr "アラート"
#: src/components/alerts/alert-button.tsx:88
#: src/components/systems-table/systems-table.tsx:317
msgid "All Systems"
msgstr "すべてのシステム"
#: src/components/systems-table/systems-table.tsx:261
msgid "Are you sure you want to delete {name}?"
msgstr "{name}を削除してもよろしいですか?"
#: src/components/command-palette.tsx:186
#: src/components/navbar.tsx:102
msgid "Auth Providers"
msgstr "認証プロバイダー"
#: src/components/copy-to-clipboard.tsx:16
msgid "Automatic copy requires a secure context."
msgstr "自動コピーには安全なコンテキストが必要です。"
#: src/components/routes/system.tsx:568
msgid "Average"
msgstr "平均"
#: src/components/routes/system.tsx:387
msgid "Average CPU utilization of containers"
msgstr "コンテナの平均CPU使用率"
#: src/components/alerts/alerts-system.tsx:204
msgid "Average exceeds <0>{value}{0}</0>"
msgstr "平均が<0>{value}{0}</0>を超えています"
#: src/components/routes/system.tsx:376
msgid "Average system-wide CPU utilization"
msgstr "システム全体の平均CPU使用率"
#: src/components/command-palette.tsx:171
#: src/components/navbar.tsx:94
msgid "Backups"
msgstr "バックアップ"
#: src/components/routes/system.tsx:436
#: src/lib/utils.ts:307
msgid "Bandwidth"
msgstr "帯域幅"
#: src/components/login/auth-form.tsx:313
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
msgstr "BeszelはOpenID Connectと多くのOAuth2認証プロバイダーをサポートしています。"
#: src/components/routes/settings/notifications.tsx:127
msgid "Beszel uses <0>Shoutrrr</0> to integrate with popular notification services."
msgstr "Beszelは<0>Shoutrrr</0>を使用して、人気のある通知サービスと統合します。"
#: src/components/add-system.tsx:88
msgid "Binary"
msgstr "バイナリ"
#: src/components/charts/mem-chart.tsx:89
msgid "Cache / Buffers"
msgstr "キャッシュ / バッファ"
#: src/components/systems-table/systems-table.tsx:272
msgid "Cancel"
msgstr "キャンセル"
#: src/components/routes/settings/config-yaml.tsx:68
msgid "Caution - potential data loss"
msgstr "注意 - データ損失の可能性"
#: src/components/routes/settings/general.tsx:36
msgid "Change general application options."
msgstr "一般的なアプリケーションオプションを変更します。"
#: src/components/routes/settings/general.tsx:78
msgid "Chart options"
msgstr "チャートオプション"
#: src/components/login/forgot-pass-form.tsx:34
msgid "Check {email} for a reset link."
msgstr "{email}を確認してリセットリンクを探してください。"
#: src/components/routes/settings/layout.tsx:40
msgid "Check logs for more details."
msgstr "詳細についてはログを確認してください。"
#: src/components/routes/settings/notifications.tsx:183
msgid "Check your notification service"
msgstr "通知サービスを確認してください"
#: src/components/add-system.tsx:153
msgid "Click to copy"
msgstr "クリックしてコピー"
#. Context: table columns
#: src/components/systems-table/systems-table.tsx:328
msgid "Columns"
msgstr "列"
#: src/components/login/forgot-pass-form.tsx:83
#: src/components/login/forgot-pass-form.tsx:89
msgid "Command line instructions"
msgstr "コマンドラインの指示"
#: src/components/routes/settings/notifications.tsx:77
msgid "Configure how you receive alert notifications."
msgstr "アラート通知の受信方法を設定します。"
#: src/components/login/auth-form.tsx:189
#: src/components/login/auth-form.tsx:194
msgid "Confirm password"
msgstr "パスワードを確認"
#: src/components/systems-table/systems-table.tsx:278
msgid "Continue"
msgstr "続行"
#: src/lib/utils.ts:25
msgid "Copied to clipboard"
msgstr "クリップボードにコピーされました"
#: src/components/add-system.tsx:164
msgid "Copy"
msgstr "コピー"
#: src/components/systems-table/systems-table.tsx:247
msgid "Copy host"
msgstr "ホストをコピー"
#: src/components/add-system.tsx:175
msgid "Copy Linux command"
msgstr "Linuxコマンドをコピー"
#: src/components/copy-to-clipboard.tsx:13
msgid "Copy text"
msgstr "テキストをコピー"
#: src/components/systems-table/systems-table.tsx:152
msgid "CPU"
msgstr "CPU"
#: src/components/charts/area-chart.tsx:52
#: src/components/routes/system.tsx:375
#: src/lib/utils.ts:289
msgid "CPU Usage"
msgstr "CPU使用率"
#: src/components/login/auth-form.tsx:215
msgid "Create account"
msgstr "アカウントを作成"
#. Dark theme
#: src/components/mode-toggle.tsx:21
msgid "Dark"
msgstr "ダーク"
#: src/components/command-palette.tsx:82
#: src/components/routes/home.tsx:35
msgid "Dashboard"
msgstr "ダッシュボード"
#: src/components/routes/settings/general.tsx:85
msgid "Default time period"
msgstr "デフォルトの期間"
#: src/components/systems-table/systems-table.tsx:253
msgid "Delete"
msgstr "削除"
#: src/components/systems-table/systems-table.tsx:166
msgid "Disk"
msgstr "ディスク"
#: src/components/routes/system.tsx:426
msgid "Disk I/O"
msgstr "ディスクI/O"
#: src/components/charts/disk-chart.tsx:74
#: src/components/routes/system.tsx:415
#: src/lib/utils.ts:301
msgid "Disk Usage"
msgstr "ディスク使用率"
#: src/components/routes/system.tsx:495
msgid "Disk usage of {extraFsName}"
msgstr "{extraFsName}のディスク使用率"
#: src/components/routes/system.tsx:386
msgid "Docker CPU Usage"
msgstr "Docker CPU使用率"
#: src/components/routes/system.tsx:407
msgid "Docker Memory Usage"
msgstr "Dockerメモリ使用率"
#: src/components/routes/system.tsx:452
msgid "Docker Network I/O"
msgstr "DockerネットワークI/O"
#: src/components/command-palette.tsx:125
msgid "Documentation"
msgstr "ドキュメント"
#: src/components/login/auth-form.tsx:158
msgid "email"
msgstr "メール"
#: src/components/login/auth-form.tsx:152
#: src/components/login/forgot-pass-form.tsx:53
msgid "Email"
msgstr "メール"
#: src/components/routes/settings/notifications.tsx:91
msgid "Email notifications"
msgstr "メール通知"
#: src/components/login/login.tsx:36
msgid "Enter email address to reset password"
msgstr "パスワードをリセットするためにメールアドレスを入力してください"
#: src/components/routes/settings/notifications.tsx:111
msgid "Enter email address..."
msgstr "メールアドレスを入力..."
#: src/components/login/auth-form.tsx:256
#: src/components/routes/settings/config-yaml.tsx:28
#: src/components/routes/settings/notifications.tsx:187
msgid "Error"
msgstr "エラー"
#: src/components/routes/home.tsx:81
msgid "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
msgstr "過去{2, plural, one {# 分} other {# 分}}で{0}{1}を超えています"
#: src/components/routes/settings/config-yaml.tsx:72
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
msgstr "<0>config.yml</0>に定義されていない既存のシステムは削除されます。定期的にバックアップを作成してください。"
#: src/components/routes/settings/config-yaml.tsx:93
msgid "Export configuration"
msgstr "設定をエクスポート"
#: src/components/routes/settings/config-yaml.tsx:48
msgid "Export your current systems configuration."
msgstr "現在のシステム設定をエクスポートします。"
#: src/lib/utils.ts:38
msgid "Failed to authenticate"
msgstr "認証に失敗しました"
#: src/components/routes/settings/layout.tsx:39
#: src/components/routes/settings/notifications.tsx:62
msgid "Failed to save settings"
msgstr "設定の保存に失敗しました"
#: src/components/routes/settings/notifications.tsx:188
msgid "Failed to send test notification"
msgstr "テスト通知の送信に失敗しました"
#: src/components/alerts/alerts-system.tsx:27
msgid "Failed to update alert"
msgstr "アラートの更新に失敗しました"
#: src/components/routes/system.tsx:539
#: src/components/systems-table/systems-table.tsx:324
msgid "Filter..."
msgstr "フィルター..."
#: src/components/alerts/alerts-system.tsx:225
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
msgstr "<0>{min}</0> {min, plural, one {分} other {分}}の間"
#: src/components/login/auth-form.tsx:337
msgid "Forgot password?"
msgstr "パスワードをお忘れですか?"
#. Context: General settings
#: src/components/routes/settings/general.tsx:33
#: src/components/routes/settings/layout.tsx:51
msgid "General"
msgstr "一般"
#: src/components/add-system.tsx:119
msgid "Host / IP"
msgstr "ホスト / IP"
#: src/components/login/forgot-pass-form.tsx:93
msgid "If you've lost the password to your admin account, you may reset it using the following command."
msgstr "管理者アカウントのパスワードを忘れた場合は、次のコマンドを使用してリセットできます。"
#: src/components/login/auth-form.tsx:16
msgid "Invalid email address."
msgstr "無効なメールアドレスです。"
#. Linux kernel
#: src/components/routes/system.tsx:254
msgid "Kernel"
msgstr "カーネル"
#: src/components/routes/settings/general.tsx:45
msgid "Language"
msgstr "言語"
#. Light theme
#: src/components/mode-toggle.tsx:16
msgid "Light"
msgstr "ライト"
#: src/components/navbar.tsx:113
msgid "Log Out"
msgstr "ログアウト"
#: src/components/login/login.tsx:17
msgid "Login"
msgstr "ログイン"
#: src/components/login/auth-form.tsx:42
#: src/components/login/forgot-pass-form.tsx:15
msgid "Login attempt failed"
msgstr "ログイン試行に失敗しました"
#: src/components/command-palette.tsx:157
#: src/components/navbar.tsx:86
msgid "Logs"
msgstr "ログ"
#: src/components/routes/settings/notifications.tsx:80
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
msgstr "アラートを作成する場所を探していますか?システムテーブルのベル<0/>アイコンをクリックしてください。"
#: src/components/routes/settings/layout.tsx:85
msgid "Manage display and notification preferences."
msgstr "表示と通知の設定を管理します。"
#. Chart select field. Please try to keep this short.
#: src/components/routes/system.tsx:571
msgid "Max 1 min"
msgstr "最大1分"
#: src/components/systems-table/systems-table.tsx:159
msgid "Memory"
msgstr "メモリ"
#: src/components/routes/system.tsx:397
#: src/lib/utils.ts:295
msgid "Memory Usage"
msgstr "メモリ使用率"
#: src/components/routes/system.tsx:408
msgid "Memory usage of docker containers"
msgstr "Dockerコンテナのメモリ使用率"
#: src/components/add-system.tsx:113
msgid "Name"
msgstr "名前"
#: src/components/systems-table/systems-table.tsx:173
msgid "Net"
msgstr "帯域"
#: src/components/routes/system.tsx:453
msgid "Network traffic of docker containers"
msgstr "Dockerコンテナのネットワークトラフィック"
#: src/components/routes/system.tsx:438
msgid "Network traffic of public interfaces"
msgstr "パブリックインターフェースのネットワークトラフィック"
#: src/components/command-palette.tsx:50
msgid "No results found."
msgstr "結果が見つかりませんでした。"
#: src/components/systems-table/systems-table.tsx:400
msgid "No systems found."
msgstr "システムが見つかりませんでした。"
#: src/components/command-palette.tsx:111
#: src/components/routes/settings/layout.tsx:56
#: src/components/routes/settings/notifications.tsx:74
msgid "Notifications"
msgstr "通知"
#: src/components/login/auth-form.tsx:308
msgid "OAuth 2 / OIDC support"
msgstr "OAuth 2 / OIDCサポート"
#: src/components/routes/settings/config-yaml.tsx:61
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
msgstr "再起動のたびに、データベース内のシステムはファイルに定義されたシステムに一致するように更新されます。"
#: src/components/systems-table/systems-table.tsx:219
msgid "Open menu"
msgstr "メニューを開く"
#: src/components/login/auth-form.tsx:227
msgid "Or continue with"
msgstr "または続行"
#: src/components/alerts/alert-button.tsx:109
msgid "Overwrite existing alerts"
msgstr "既存のアラートを上書き"
#: src/components/command-palette.tsx:85
msgid "Page"
msgstr "ページ"
#: src/components/command-palette.tsx:72
msgid "Pages / Settings"
msgstr "ページ / 設定"
#: src/components/login/auth-form.tsx:171
#: src/components/login/auth-form.tsx:176
msgid "Password"
msgstr "パスワード"
#: src/components/login/auth-form.tsx:17
msgid "Password must be at least 10 characters."
msgstr "パスワードは10文字以上でなければなりません。"
#: src/components/login/forgot-pass-form.tsx:33
msgid "Password reset request received"
msgstr "パスワードリセットのリクエストを受け取りました"
#: src/components/systems-table/systems-table.tsx:241
msgid "Pause"
msgstr "一時停止"
#: src/components/routes/settings/notifications.tsx:95
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
msgstr "アラートが配信されるように<0>SMTPサーバーを設定</0>してください。"
#: src/components/alerts/alerts-system.tsx:28
msgid "Please check logs for more details."
msgstr "詳細についてはログを確認してください。"
#: src/components/login/auth-form.tsx:43
#: src/components/login/forgot-pass-form.tsx:16
msgid "Please check your credentials and try again"
msgstr "資格情報を確認して再試行してください"
#: src/components/login/login.tsx:34
msgid "Please create an admin account"
msgstr "管理者アカウントを作成してください"
#: src/components/login/auth-form.tsx:257
msgid "Please enable pop-ups for this site"
msgstr "このサイトのポップアップを有効にしてください"
#: src/lib/utils.ts:39
msgid "Please log in again"
msgstr "再度ログインしてください"
#: src/components/login/auth-form.tsx:316
msgid "Please see <0>the documentation</0> for instructions."
msgstr "手順については<0>ドキュメント</0>を参照してください。"
#: src/components/login/login.tsx:38
msgid "Please sign in to your account"
msgstr "アカウントにサインインしてください"
#: src/components/add-system.tsx:125
msgid "Port"
msgstr "ポート"
#: src/components/routes/system.tsx:398
msgid "Precise utilization at the recorded time"
msgstr "記録された時点での正確な利用"
#: src/components/routes/settings/general.tsx:58
msgid "Preferred Language"
msgstr "優先言語"
#. Use 'Key' if your language requires many more characters
#: src/components/add-system.tsx:131
msgid "Public Key"
msgstr "公開鍵"
#. Context is disk read
#: src/components/charts/area-chart.tsx:56
#: src/components/charts/area-chart.tsx:66
msgid "Read"
msgstr "読み取り"
#. Context is network bytes received (download)
#: src/components/charts/area-chart.tsx:61
msgid "Received"
msgstr "受信"
#: src/components/login/forgot-pass-form.tsx:76
msgid "Reset Password"
msgstr "パスワードをリセット"
#: src/components/systems-table/systems-table.tsx:236
msgid "Resume"
msgstr "再開"
#: src/components/routes/settings/notifications.tsx:117
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
msgstr "Enterキーまたはカンマを使用してアドレスを保存します。空白のままにするとメール通知が無効になります。"
#: src/components/routes/settings/general.tsx:106
#: src/components/routes/settings/notifications.tsx:167
msgid "Save Settings"
msgstr "設定を保存"
#: src/components/navbar.tsx:142
msgid "Search"
msgstr "検索"
#: src/components/command-palette.tsx:47
msgid "Search for systems or settings..."
msgstr "システムまたは設定を検索..."
#: src/components/alerts/alert-button.tsx:71
msgid "See <0>notification settings</0> to configure how you receive alerts."
msgstr "アラートの受信方法を設定するには<0>通知設定</0>を参照してください。"
#. Context is network bytes sent (upload)
#: src/components/charts/area-chart.tsx:60
msgid "Sent"
msgstr "送信"
#: src/components/routes/settings/general.tsx:100
msgid "Sets the default time range for charts when a system is viewed."
msgstr "システムを表示する際のチャートのデフォルトの時間範囲を設定します。"
#: src/components/command-palette.tsx:96
#: src/components/command-palette.tsx:99
#: src/components/command-palette.tsx:114
#: src/components/routes/settings/layout.tsx:71
#: src/components/routes/settings/layout.tsx:82
msgid "Settings"
msgstr "設定"
#: src/components/routes/settings/layout.tsx:33
msgid "Settings saved"
msgstr "設定が保存されました"
#: src/components/login/auth-form.tsx:215
msgid "Sign in"
msgstr "サインイン"
#: src/components/command-palette.tsx:201
msgid "SMTP settings"
msgstr "SMTP設定"
#: src/lib/utils.ts:282
msgid "Status"
msgstr "ステータス"
#: src/components/routes/system.tsx:467
msgid "Swap space used by the system"
msgstr "システムが使用するスワップ領域"
#: src/components/routes/system.tsx:466
msgid "Swap Usage"
msgstr "スワップ使用量"
#. System theme
#: src/components/mode-toggle.tsx:26
#: src/components/systems-table/systems-table.tsx:110
#: src/components/systems-table/systems-table.tsx:121
msgid "System"
msgstr "システム"
#: src/components/navbar.tsx:78
msgid "Systems"
msgstr "システム"
#: src/components/routes/settings/config-yaml.tsx:55
msgid "Systems may be managed in a <0>config.yml</0> file inside your data directory."
msgstr "システムはデータディレクトリ内の<0>config.yml</0>ファイルで管理できます。"
#: src/components/routes/system.tsx:477
#: src/lib/utils.ts:314
msgid "Temperature"
msgstr "温度"
#: src/components/routes/system.tsx:478
msgid "Temperatures of system sensors"
msgstr "システムセンサーの温度"
#: src/components/routes/settings/notifications.tsx:211
msgid "Test <0>URL</0>"
msgstr "テスト<0>URL</0>"
#: src/components/routes/settings/notifications.tsx:182
msgid "Test notification sent"
msgstr "テスト通知が送信されました"
#: src/components/add-system.tsx:104
msgid "The agent must be running on the system to connect. Copy the installation command for the agent below."
msgstr "接続するにはエージェントがシステム上で実行されている必要があります。以下のエージェントのインストールコマンドをコピーしてください。"
#: src/components/add-system.tsx:95
msgid "The agent must be running on the system to connect. Copy the<0>docker-compose.yml</0> for the agent below."
msgstr "接続するにはエージェントがシステム上で実行されている必要があります。以下のエージェント用<0>docker-compose.yml</0>をコピーしてください。"
#: src/components/login/forgot-pass-form.tsx:98
msgid "Then log into the backend and reset your user account password in the users table."
msgstr "その後、バックエンドにログインして、ユーザーテーブルでユーザーアカウントのパスワードをリセットしてください。"
#: src/components/systems-table/systems-table.tsx:264
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
msgstr "この操作は元に戻せません。これにより、データベースから{name}のすべての現在のレコードが永久に削除されます。"
#: src/components/routes/system.tsx:507
msgid "Throughput of {extraFsName}"
msgstr "{extraFsName}のスループット"
#: src/components/routes/system.tsx:427
msgid "Throughput of root filesystem"
msgstr "ルートファイルシステムのスループット"
#: src/components/routes/settings/notifications.tsx:106
msgid "To email(s)"
msgstr "メール宛"
#: src/components/routes/system.tsx:350
#: src/components/routes/system.tsx:363
msgid "Toggle grid"
msgstr "グリッドを切り替え"
#: src/components/mode-toggle.tsx:33
msgid "Toggle theme"
msgstr "テーマを切り替え"
#: src/lib/utils.ts:317
msgid "Triggers when any sensor exceeds a threshold"
msgstr "センサーがしきい値を超えたときにトリガーされます"
#: src/lib/utils.ts:310
msgid "Triggers when combined up/down exceeds a threshold"
msgstr "上り/下りの合計がしきい値を超えたときにトリガーされます"
#: src/lib/utils.ts:292
msgid "Triggers when CPU usage exceeds a threshold"
msgstr "CPU使用率がしきい値を超えたときにトリガーされます"
#: src/lib/utils.ts:298
msgid "Triggers when memory usage exceeds a threshold"
msgstr "メモリ使用率がしきい値を超えたときにトリガーされます"
#: src/lib/utils.ts:285
msgid "Triggers when status switches between up and down"
msgstr "ステータスが上から下に切り替わるときにトリガーされます"
#: src/lib/utils.ts:304
msgid "Triggers when usage of any disk exceeds a threshold"
msgstr "ディスクの使用量がしきい値を超えたときにトリガーされます"
#: src/components/systems-table/systems-table.tsx:320
msgid "Updated in real time. Click on a system to view information."
msgstr "リアルタイムで更新されます。システムをクリックして情報を表示します。"
#: src/components/routes/system.tsx:253
msgid "Uptime"
msgstr "稼働時間"
#: src/components/routes/system.tsx:494
msgid "Usage"
msgstr "使用量"
#: src/components/routes/system.tsx:415
msgid "Usage of root partition"
msgstr "ルートパーティションの使用量"
#: src/components/charts/mem-chart.tsx:65
#: src/components/charts/swap-chart.tsx:56
msgid "Used"
msgstr "使用済み"
#: src/components/login/auth-form.tsx:138
msgid "username"
msgstr "ユーザー名"
#: src/components/login/auth-form.tsx:131
msgid "Username"
msgstr "ユーザー名"
#: src/components/command-palette.tsx:143
#: src/components/navbar.tsx:70
msgid "Users"
msgstr "ユーザー"
#: src/components/routes/system.tsx:603
msgid "Waiting for enough records to display"
msgstr "表示するのに十分なレコードを待っています"
#: src/components/routes/settings/general.tsx:48
msgid "Want to help us make our translations even better? Check out <0>Crowdin</0> for more details."
msgstr "翻訳をさらに良くするためにご協力いただけますか?詳細については<0>Crowdin</0>をご覧ください。"
#: src/components/routes/settings/notifications.tsx:124
msgid "Webhook / Push notifications"
msgstr "Webhook / プッシュ通知"
#. Context is disk write
#: src/components/charts/area-chart.tsx:55
#: src/components/charts/area-chart.tsx:65
msgid "Write"
msgstr "書き込み"
#: src/components/routes/settings/layout.tsx:61
msgid "YAML Config"
msgstr "YAML設定"
#: src/components/routes/settings/config-yaml.tsx:45
msgid "YAML Configuration"
msgstr "YAML設定"
#: src/components/routes/settings/layout.tsx:34
msgid "Your user settings have been updated."
msgstr "ユーザー設定が更新されました。"

View File

@@ -0,0 +1,808 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2024-11-01 11:30-0400\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: ko\n"
"Project-Id-Version: beszel\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-11-04 20:46\n"
"Last-Translator: \n"
"Language-Team: Korean\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Crowdin-Project: beszel\n"
"X-Crowdin-Project-ID: 733311\n"
"X-Crowdin-Language: ko\n"
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 16\n"
#: src/components/routes/system.tsx:242
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# 일} other {# 일}}"
#: src/components/routes/system.tsx:240
msgid "{hours, plural, one {# hour} other {# hours}}"
msgstr "{hours, plural, one {# 시간} other {# 시간}}"
#: src/lib/utils.ts:139
msgid "1 hour"
msgstr "1시간"
#: src/lib/utils.ts:162
msgid "1 week"
msgstr "1주"
#: src/lib/utils.ts:147
msgid "12 hours"
msgstr "12시간"
#: src/lib/utils.ts:155
msgid "24 hours"
msgstr "24시간"
#: src/lib/utils.ts:170
msgid "30 days"
msgstr "30일"
#. Table column
#: src/components/systems-table/systems-table.tsx:207
msgid "Actions"
msgstr "작업"
#: src/components/routes/home.tsx:62
msgid "Active Alerts"
msgstr "활성 경고"
#: src/components/add-system.tsx:74
msgid "Add <0>System</0>"
msgstr "<0>시스템</0> 추가"
#: src/components/add-system.tsx:83
msgid "Add New System"
msgstr "새 시스템 추가"
#: src/components/add-system.tsx:167
#: src/components/add-system.tsx:178
msgid "Add system"
msgstr "시스템 추가"
#: src/components/routes/settings/notifications.tsx:156
msgid "Add URL"
msgstr "URL 추가"
#: src/components/routes/settings/general.tsx:81
msgid "Adjust display options for charts."
msgstr "차트의 표시 옵션 조정."
#: src/components/command-palette.tsx:133
#: src/components/command-palette.tsx:146
#: src/components/command-palette.tsx:160
#: src/components/command-palette.tsx:174
#: src/components/command-palette.tsx:189
#: src/components/command-palette.tsx:204
msgid "Admin"
msgstr "관리자"
#: src/components/systems-table/systems-table.tsx:186
msgid "Agent"
msgstr "에이젠"
#: src/components/alerts/alert-button.tsx:32
#: src/components/alerts/alert-button.tsx:68
msgid "Alerts"
msgstr "경고"
#: src/components/alerts/alert-button.tsx:88
#: src/components/systems-table/systems-table.tsx:317
msgid "All Systems"
msgstr "모든 시스템"
#: src/components/systems-table/systems-table.tsx:261
msgid "Are you sure you want to delete {name}?"
msgstr "{name}을(를) 삭제하시겠습니까?"
#: src/components/command-palette.tsx:186
#: src/components/navbar.tsx:102
msgid "Auth Providers"
msgstr "인증 제공자"
#: src/components/copy-to-clipboard.tsx:16
msgid "Automatic copy requires a secure context."
msgstr "자동 복사는 안전한 컨텍스트가 필요합니다."
#: src/components/routes/system.tsx:568
msgid "Average"
msgstr "평균"
#: src/components/routes/system.tsx:387
msgid "Average CPU utilization of containers"
msgstr "컨테이너의 평균 CPU 사용량"
#: src/components/alerts/alerts-system.tsx:204
msgid "Average exceeds <0>{value}{0}</0>"
msgstr "평균이 <0>{value}{0}</0>을 초과합니다"
#: src/components/routes/system.tsx:376
msgid "Average system-wide CPU utilization"
msgstr "시스템 전체의 평균 CPU 사용량"
#: src/components/command-palette.tsx:171
#: src/components/navbar.tsx:94
msgid "Backups"
msgstr "백업"
#: src/components/routes/system.tsx:436
#: src/lib/utils.ts:307
msgid "Bandwidth"
msgstr "대역폭"
#: src/components/login/auth-form.tsx:313
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
msgstr "Beszel은 OpenID Connect 및 많은 OAuth2 인증 제공자를 지원합니다."
#: src/components/routes/settings/notifications.tsx:127
msgid "Beszel uses <0>Shoutrrr</0> to integrate with popular notification services."
msgstr "Beszel은 <0>Shoutrrr</0>을 사용하여 인기 있는 알림 서비스와 통합합니다."
#: src/components/add-system.tsx:88
msgid "Binary"
msgstr "이진"
#: src/components/charts/mem-chart.tsx:89
msgid "Cache / Buffers"
msgstr "캐시 / 버퍼"
#: src/components/systems-table/systems-table.tsx:272
msgid "Cancel"
msgstr "취소"
#: src/components/routes/settings/config-yaml.tsx:68
msgid "Caution - potential data loss"
msgstr "주의 - 데이터 손실 가능성"
#: src/components/routes/settings/general.tsx:36
msgid "Change general application options."
msgstr "일반 애플리케이션 옵션 변경."
#: src/components/routes/settings/general.tsx:78
msgid "Chart options"
msgstr "차트 옵션"
#: src/components/login/forgot-pass-form.tsx:34
msgid "Check {email} for a reset link."
msgstr "{email}에서 재설정 링크를 확인하세요."
#: src/components/routes/settings/layout.tsx:40
msgid "Check logs for more details."
msgstr "자세한 내용은 로그를 확인하세요."
#: src/components/routes/settings/notifications.tsx:183
msgid "Check your notification service"
msgstr "알림 서비스를 확인하세요."
#: src/components/add-system.tsx:153
msgid "Click to copy"
msgstr "클릭하여 복사"
#. Context: table columns
#: src/components/systems-table/systems-table.tsx:328
msgid "Columns"
msgstr "열"
#: src/components/login/forgot-pass-form.tsx:83
#: src/components/login/forgot-pass-form.tsx:89
msgid "Command line instructions"
msgstr "명령줄 지침"
#: src/components/routes/settings/notifications.tsx:77
msgid "Configure how you receive alert notifications."
msgstr "경고 알림을 받는 방법을 구성하세요."
#: src/components/login/auth-form.tsx:189
#: src/components/login/auth-form.tsx:194
msgid "Confirm password"
msgstr "비밀번호 확인"
#: src/components/systems-table/systems-table.tsx:278
msgid "Continue"
msgstr "계속"
#: src/lib/utils.ts:25
msgid "Copied to clipboard"
msgstr "클립보드에 복사됨"
#: src/components/add-system.tsx:164
msgid "Copy"
msgstr "복사"
#: src/components/systems-table/systems-table.tsx:247
msgid "Copy host"
msgstr "호스트 복사"
#: src/components/add-system.tsx:175
msgid "Copy Linux command"
msgstr "리눅스 명령 복사"
#: src/components/copy-to-clipboard.tsx:13
msgid "Copy text"
msgstr "텍스트 복사"
#: src/components/systems-table/systems-table.tsx:152
msgid "CPU"
msgstr "CPU"
#: src/components/charts/area-chart.tsx:52
#: src/components/routes/system.tsx:375
#: src/lib/utils.ts:289
msgid "CPU Usage"
msgstr "CPU 사용량"
#: src/components/login/auth-form.tsx:215
msgid "Create account"
msgstr "계정 생성"
#. Dark theme
#: src/components/mode-toggle.tsx:21
msgid "Dark"
msgstr "어두운"
#: src/components/command-palette.tsx:82
#: src/components/routes/home.tsx:35
msgid "Dashboard"
msgstr "대시보드"
#: src/components/routes/settings/general.tsx:85
msgid "Default time period"
msgstr "기본 시간 기간"
#: src/components/systems-table/systems-table.tsx:253
msgid "Delete"
msgstr "삭제"
#: src/components/systems-table/systems-table.tsx:166
msgid "Disk"
msgstr "디스크"
#: src/components/routes/system.tsx:426
msgid "Disk I/O"
msgstr "디스크 I/O"
#: src/components/charts/disk-chart.tsx:74
#: src/components/routes/system.tsx:415
#: src/lib/utils.ts:301
msgid "Disk Usage"
msgstr "디스크 사용량"
#: src/components/routes/system.tsx:495
msgid "Disk usage of {extraFsName}"
msgstr "{extraFsName}의 디스크 사용량"
#: src/components/routes/system.tsx:386
msgid "Docker CPU Usage"
msgstr "도커 CPU 사용량"
#: src/components/routes/system.tsx:407
msgid "Docker Memory Usage"
msgstr "도커 메모리 사용량"
#: src/components/routes/system.tsx:452
msgid "Docker Network I/O"
msgstr "도커 네트워크 I/O"
#: src/components/command-palette.tsx:125
msgid "Documentation"
msgstr "문서"
#: src/components/login/auth-form.tsx:158
msgid "email"
msgstr "이메일"
#: src/components/login/auth-form.tsx:152
#: src/components/login/forgot-pass-form.tsx:53
msgid "Email"
msgstr "이메일"
#: src/components/routes/settings/notifications.tsx:91
msgid "Email notifications"
msgstr "이메일 알림"
#: src/components/login/login.tsx:36
msgid "Enter email address to reset password"
msgstr "비밀번호를 재설정하려면 이메일 주소를 입력하세요"
#: src/components/routes/settings/notifications.tsx:111
msgid "Enter email address..."
msgstr "이메일 주소 입력..."
#: src/components/login/auth-form.tsx:256
#: src/components/routes/settings/config-yaml.tsx:28
#: src/components/routes/settings/notifications.tsx:187
msgid "Error"
msgstr "오류"
#: src/components/routes/home.tsx:81
msgid "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
msgstr "마지막 {2, plural, one {# 분} other {# 분}} 동안 {0}{1} 초과"
#: src/components/routes/settings/config-yaml.tsx:72
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
msgstr "<0>config.yml</0>에 정의되지 않은 기존 시스템은 삭제됩니다. 정기적으로 백업을 하세요."
#: src/components/routes/settings/config-yaml.tsx:93
msgid "Export configuration"
msgstr "구성 내보내기"
#: src/components/routes/settings/config-yaml.tsx:48
msgid "Export your current systems configuration."
msgstr "현재 시스템 구성을 내보내기."
#: src/lib/utils.ts:38
msgid "Failed to authenticate"
msgstr "인증 실패"
#: src/components/routes/settings/layout.tsx:39
#: src/components/routes/settings/notifications.tsx:62
msgid "Failed to save settings"
msgstr "설정 저장 실패"
#: src/components/routes/settings/notifications.tsx:188
msgid "Failed to send test notification"
msgstr "테스트 알림 전송 실패"
#: src/components/alerts/alerts-system.tsx:27
msgid "Failed to update alert"
msgstr "경고 업데이트 실패"
#: src/components/routes/system.tsx:539
#: src/components/systems-table/systems-table.tsx:324
msgid "Filter..."
msgstr "필터..."
#: src/components/alerts/alerts-system.tsx:225
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
msgstr "<0>{min}</0> {min, plural, one {분} other {분}} 동안"
#: src/components/login/auth-form.tsx:337
msgid "Forgot password?"
msgstr "비밀번호를 잊으셨나요?"
#. Context: General settings
#: src/components/routes/settings/general.tsx:33
#: src/components/routes/settings/layout.tsx:51
msgid "General"
msgstr "일반"
#: src/components/add-system.tsx:119
msgid "Host / IP"
msgstr "호스트 / IP"
#: src/components/login/forgot-pass-form.tsx:93
msgid "If you've lost the password to your admin account, you may reset it using the following command."
msgstr "관리자 계정의 비밀번호를 잃어버린 경우, 다음 명령을 사용하여 재설정할 수 있습니다."
#: src/components/login/auth-form.tsx:16
msgid "Invalid email address."
msgstr "잘못된 이메일 주소입니다."
#. Linux kernel
#: src/components/routes/system.tsx:254
msgid "Kernel"
msgstr "커널"
#: src/components/routes/settings/general.tsx:45
msgid "Language"
msgstr "언어"
#. Light theme
#: src/components/mode-toggle.tsx:16
msgid "Light"
msgstr "밝은"
#: src/components/navbar.tsx:113
msgid "Log Out"
msgstr "로그아웃"
#: src/components/login/login.tsx:17
msgid "Login"
msgstr "로그인"
#: src/components/login/auth-form.tsx:42
#: src/components/login/forgot-pass-form.tsx:15
msgid "Login attempt failed"
msgstr "로그인 시도 실패"
#: src/components/command-palette.tsx:157
#: src/components/navbar.tsx:86
msgid "Logs"
msgstr "로그"
#: src/components/routes/settings/notifications.tsx:80
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
msgstr "경고를 생성할 위치를 찾고 계신가요? 시스템 테이블의 종 <0/> 아이콘을 클릭하세요."
#: src/components/routes/settings/layout.tsx:85
msgid "Manage display and notification preferences."
msgstr "디스플레이 및 알림 환경설정을 관리하세요."
#. Chart select field. Please try to keep this short.
#: src/components/routes/system.tsx:571
msgid "Max 1 min"
msgstr "최대 1분"
#: src/components/systems-table/systems-table.tsx:159
msgid "Memory"
msgstr "메모리"
#: src/components/routes/system.tsx:397
#: src/lib/utils.ts:295
msgid "Memory Usage"
msgstr "메모리 사용량"
#: src/components/routes/system.tsx:408
msgid "Memory usage of docker containers"
msgstr "도커 컨테이너의 메모리 사용량"
#: src/components/add-system.tsx:113
msgid "Name"
msgstr "이름"
#: src/components/systems-table/systems-table.tsx:173
msgid "Net"
msgstr "네트"
#: src/components/routes/system.tsx:453
msgid "Network traffic of docker containers"
msgstr "도커 컨테이너의 네트워크 트래픽"
#: src/components/routes/system.tsx:438
msgid "Network traffic of public interfaces"
msgstr "공용 인터페이스의 네트워크 트래픽"
#: src/components/command-palette.tsx:50
msgid "No results found."
msgstr "결과가 없습니다."
#: src/components/systems-table/systems-table.tsx:400
msgid "No systems found."
msgstr "시스템을 찾을 수 없습니다."
#: src/components/command-palette.tsx:111
#: src/components/routes/settings/layout.tsx:56
#: src/components/routes/settings/notifications.tsx:74
msgid "Notifications"
msgstr "알림"
#: src/components/login/auth-form.tsx:308
msgid "OAuth 2 / OIDC support"
msgstr "OAuth 2 / OIDC 지원"
#: src/components/routes/settings/config-yaml.tsx:61
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
msgstr "각 재시작 시, 데이터베이스의 시스템이 파일에 정의된 시스템과 일치하도록 업데이트됩니다."
#: src/components/systems-table/systems-table.tsx:219
msgid "Open menu"
msgstr "메뉴 열기"
#: src/components/login/auth-form.tsx:227
msgid "Or continue with"
msgstr "또는 계속하기"
#: src/components/alerts/alert-button.tsx:109
msgid "Overwrite existing alerts"
msgstr "기존 경고 덮어쓰기"
#: src/components/command-palette.tsx:85
msgid "Page"
msgstr "페이지"
#: src/components/command-palette.tsx:72
msgid "Pages / Settings"
msgstr "페이지 / 설정"
#: src/components/login/auth-form.tsx:171
#: src/components/login/auth-form.tsx:176
msgid "Password"
msgstr "비밀번호"
#: src/components/login/auth-form.tsx:17
msgid "Password must be at least 10 characters."
msgstr "비밀번호는 최소 10자 이상이어야 합니다."
#: src/components/login/forgot-pass-form.tsx:33
msgid "Password reset request received"
msgstr "비밀번호 재설정 요청이 접수되었습니다"
#: src/components/systems-table/systems-table.tsx:241
msgid "Pause"
msgstr "일시 중지"
#: src/components/routes/settings/notifications.tsx:95
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
msgstr "경고가 전달되도록 <0>SMTP 서버를 구성</0>하세요."
#: src/components/alerts/alerts-system.tsx:28
msgid "Please check logs for more details."
msgstr "자세한 내용은 로그를 확인하세요."
#: src/components/login/auth-form.tsx:43
#: src/components/login/forgot-pass-form.tsx:16
msgid "Please check your credentials and try again"
msgstr "자격 증명을 확인하고 다시 시도하세요."
#: src/components/login/login.tsx:34
msgid "Please create an admin account"
msgstr "관리자 계정을 생성하세요."
#: src/components/login/auth-form.tsx:257
msgid "Please enable pop-ups for this site"
msgstr "이 사이트에 대한 팝업을 활성화하세요."
#: src/lib/utils.ts:39
msgid "Please log in again"
msgstr "다시 로그인하세요."
#: src/components/login/auth-form.tsx:316
msgid "Please see <0>the documentation</0> for instructions."
msgstr "지침은 <0>문서</0>를 참조하세요."
#: src/components/login/login.tsx:38
msgid "Please sign in to your account"
msgstr "계정에 로그인하세요."
#: src/components/add-system.tsx:125
msgid "Port"
msgstr "포트"
#: src/components/routes/system.tsx:398
msgid "Precise utilization at the recorded time"
msgstr "기록된 시간의 정확한 사용량"
#: src/components/routes/settings/general.tsx:58
msgid "Preferred Language"
msgstr "선호 언어"
#. Use 'Key' if your language requires many more characters
#: src/components/add-system.tsx:131
msgid "Public Key"
msgstr "공개 키"
#. Context is disk read
#: src/components/charts/area-chart.tsx:56
#: src/components/charts/area-chart.tsx:66
msgid "Read"
msgstr "읽기"
#. Context is network bytes received (download)
#: src/components/charts/area-chart.tsx:61
msgid "Received"
msgstr "수신됨"
#: src/components/login/forgot-pass-form.tsx:76
msgid "Reset Password"
msgstr "비밀번호 재설정"
#: src/components/systems-table/systems-table.tsx:236
msgid "Resume"
msgstr "재개"
#: src/components/routes/settings/notifications.tsx:117
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
msgstr "Enter 키 또는 쉼표를 사용하여 주소를 저장하세요. 이메일 알림을 비활성화하려면 비워 두세요."
#: src/components/routes/settings/general.tsx:106
#: src/components/routes/settings/notifications.tsx:167
msgid "Save Settings"
msgstr "설정 저장"
#: src/components/navbar.tsx:142
msgid "Search"
msgstr "검색"
#: src/components/command-palette.tsx:47
msgid "Search for systems or settings..."
msgstr "시스템 또는 설정 검색..."
#: src/components/alerts/alert-button.tsx:71
msgid "See <0>notification settings</0> to configure how you receive alerts."
msgstr "경고를 받는 방법을 구성하려면 <0>알림 설정</0>을 참조하세요."
#. Context is network bytes sent (upload)
#: src/components/charts/area-chart.tsx:60
msgid "Sent"
msgstr "보냄"
#: src/components/routes/settings/general.tsx:100
msgid "Sets the default time range for charts when a system is viewed."
msgstr "시스템을 볼 때 차트의 기본 시간 범위를 설정합니다."
#: src/components/command-palette.tsx:96
#: src/components/command-palette.tsx:99
#: src/components/command-palette.tsx:114
#: src/components/routes/settings/layout.tsx:71
#: src/components/routes/settings/layout.tsx:82
msgid "Settings"
msgstr "설정"
#: src/components/routes/settings/layout.tsx:33
msgid "Settings saved"
msgstr "설정이 저장되었습니다."
#: src/components/login/auth-form.tsx:215
msgid "Sign in"
msgstr "로그인"
#: src/components/command-palette.tsx:201
msgid "SMTP settings"
msgstr "SMTP 설정"
#: src/lib/utils.ts:282
msgid "Status"
msgstr "상태"
#: src/components/routes/system.tsx:467
msgid "Swap space used by the system"
msgstr "시스템에서 사용된 스왑 공간"
#: src/components/routes/system.tsx:466
msgid "Swap Usage"
msgstr "스왑 사용량"
#. System theme
#: src/components/mode-toggle.tsx:26
#: src/components/systems-table/systems-table.tsx:110
#: src/components/systems-table/systems-table.tsx:121
msgid "System"
msgstr "시스템"
#: src/components/navbar.tsx:78
msgid "Systems"
msgstr "시스템"
#: src/components/routes/settings/config-yaml.tsx:55
msgid "Systems may be managed in a <0>config.yml</0> file inside your data directory."
msgstr "시스템은 데이터 디렉토리 내의 <0>config.yml</0> 파일에서 관리할 수 있습니다."
#: src/components/routes/system.tsx:477
#: src/lib/utils.ts:314
msgid "Temperature"
msgstr "온도"
#: src/components/routes/system.tsx:478
msgid "Temperatures of system sensors"
msgstr "시스템 센서의 온도"
#: src/components/routes/settings/notifications.tsx:211
msgid "Test <0>URL</0>"
msgstr "테스트 <0>URL</0>"
#: src/components/routes/settings/notifications.tsx:182
msgid "Test notification sent"
msgstr "테스트 알림이 전송되었습니다."
#: src/components/add-system.tsx:104
msgid "The agent must be running on the system to connect. Copy the installation command for the agent below."
msgstr "에이전트가 시스템에서 실행 중이어야 연결할 수 있습니다. 아래의 에이전트 설치 명령을 복사하세요."
#: src/components/add-system.tsx:95
msgid "The agent must be running on the system to connect. Copy the<0>docker-compose.yml</0> for the agent below."
msgstr "에이전트가 시스템에서 실행 중이어야 연결할 수 있습니다. 아래의 <0>docker-compose.yml</0>을 복사하세요."
#: src/components/login/forgot-pass-form.tsx:98
msgid "Then log into the backend and reset your user account password in the users table."
msgstr "그런 다음 백엔드에 로그인하여 사용자 테이블에서 사용자 계정 비밀번호를 재설정하세요."
#: src/components/systems-table/systems-table.tsx:264
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
msgstr "이 작업은 되돌릴 수 없습니다. 데이터베이스에서 {name}에 대한 모든 현재 기록이 영구적으로 삭제됩니다."
#: src/components/routes/system.tsx:507
msgid "Throughput of {extraFsName}"
msgstr "{extraFsName}의 처리량"
#: src/components/routes/system.tsx:427
msgid "Throughput of root filesystem"
msgstr "루트 파일 시스템의 처리량"
#: src/components/routes/settings/notifications.tsx:106
msgid "To email(s)"
msgstr "이메일로"
#: src/components/routes/system.tsx:350
#: src/components/routes/system.tsx:363
msgid "Toggle grid"
msgstr "그리드 전환"
#: src/components/mode-toggle.tsx:33
msgid "Toggle theme"
msgstr "테마 전환"
#: src/lib/utils.ts:317
msgid "Triggers when any sensor exceeds a threshold"
msgstr "센서가 임계값을 초과할 때 트리거됩니다."
#: src/lib/utils.ts:310
msgid "Triggers when combined up/down exceeds a threshold"
msgstr "상승/하강이 결합되어 임계값을 초과할 때 트리거됩니다."
#: src/lib/utils.ts:292
msgid "Triggers when CPU usage exceeds a threshold"
msgstr "CPU 사용량이 임계값을 초과할 때 트리거됩니다."
#: src/lib/utils.ts:298
msgid "Triggers when memory usage exceeds a threshold"
msgstr "메모리 사용량이 임계값을 초과할 때 트리거됩니다."
#: src/lib/utils.ts:285
msgid "Triggers when status switches between up and down"
msgstr "상태가 상승과 하강 사이에서 전환될 때 트리거됩니다."
#: src/lib/utils.ts:304
msgid "Triggers when usage of any disk exceeds a threshold"
msgstr "디스크 사용량이 임계값을 초과할 때 트리거됩니다."
#: src/components/systems-table/systems-table.tsx:320
msgid "Updated in real time. Click on a system to view information."
msgstr "실시간으로 업데이트됩니다. 시스템을 클릭하여 정보를 확인하세요."
#: src/components/routes/system.tsx:253
msgid "Uptime"
msgstr "가동 시간"
#: src/components/routes/system.tsx:494
msgid "Usage"
msgstr "사용량"
#: src/components/routes/system.tsx:415
msgid "Usage of root partition"
msgstr "루트 파티션의 사용량"
#: src/components/charts/mem-chart.tsx:65
#: src/components/charts/swap-chart.tsx:56
msgid "Used"
msgstr "사용됨"
#: src/components/login/auth-form.tsx:138
msgid "username"
msgstr "사용자 이름"
#: src/components/login/auth-form.tsx:131
msgid "Username"
msgstr "사용자 이름"
#: src/components/command-palette.tsx:143
#: src/components/navbar.tsx:70
msgid "Users"
msgstr "사용자"
#: src/components/routes/system.tsx:603
msgid "Waiting for enough records to display"
msgstr "표시할 충분한 기록을 기다리는 중"
#: src/components/routes/settings/general.tsx:48
msgid "Want to help us make our translations even better? Check out <0>Crowdin</0> for more details."
msgstr "번역을 더 좋게 만드는 데 도움을 주시겠습니까? 자세한 내용은 <0>Crowdin</0>을 확인하세요."
#: src/components/routes/settings/notifications.tsx:124
msgid "Webhook / Push notifications"
msgstr "Webhook / 푸시 알림"
#. Context is disk write
#: src/components/charts/area-chart.tsx:55
#: src/components/charts/area-chart.tsx:65
msgid "Write"
msgstr "쓰기"
#: src/components/routes/settings/layout.tsx:61
msgid "YAML Config"
msgstr "YAML 구성"
#: src/components/routes/settings/config-yaml.tsx:45
msgid "YAML Configuration"
msgstr "YAML 구성"
#: src/components/routes/settings/layout.tsx:34
msgid "Your user settings have been updated."
msgstr "사용자 설정이 업데이트되었습니다."

View File

@@ -0,0 +1,809 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2024-11-01 11:30-0400\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: pl\n"
"Project-Id-Version: beszel\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-11-04 21:51\n"
"Last-Translator: \n"
"Language-Team: Polish\n"
"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
"X-Crowdin-Project: beszel\n"
"X-Crowdin-Project-ID: 733311\n"
"X-Crowdin-Language: pl\n"
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 16\n"
#: src/components/routes/system.tsx:242
msgid "{0, plural, one {# day} other {# days}}"
msgstr ""
#: src/components/routes/system.tsx:240
msgid "{hours, plural, one {# hour} other {# hours}}"
msgstr ""
#: src/lib/utils.ts:139
msgid "1 hour"
msgstr ""
#: src/lib/utils.ts:162
msgid "1 week"
msgstr ""
#: src/lib/utils.ts:147
msgid "12 hours"
msgstr ""
#: src/lib/utils.ts:155
msgid "24 hours"
msgstr ""
#: src/lib/utils.ts:170
msgid "30 days"
msgstr ""
#. Table column
#: src/components/systems-table/systems-table.tsx:207
msgid "Actions"
msgstr ""
#: src/components/routes/home.tsx:62
msgid "Active Alerts"
msgstr ""
#: src/components/add-system.tsx:74
msgid "Add <0>System</0>"
msgstr ""
#: src/components/add-system.tsx:83
msgid "Add New System"
msgstr ""
#: src/components/add-system.tsx:167
#: src/components/add-system.tsx:178
msgid "Add system"
msgstr ""
#: src/components/routes/settings/notifications.tsx:156
msgid "Add URL"
msgstr ""
#: src/components/routes/settings/general.tsx:81
msgid "Adjust display options for charts."
msgstr ""
#: src/components/command-palette.tsx:133
#: src/components/command-palette.tsx:146
#: src/components/command-palette.tsx:160
#: src/components/command-palette.tsx:174
#: src/components/command-palette.tsx:189
#: src/components/command-palette.tsx:204
msgid "Admin"
msgstr ""
#: src/components/systems-table/systems-table.tsx:186
msgid "Agent"
msgstr ""
#: src/components/alerts/alert-button.tsx:32
#: src/components/alerts/alert-button.tsx:68
msgid "Alerts"
msgstr ""
#: src/components/alerts/alert-button.tsx:88
#: src/components/systems-table/systems-table.tsx:317
msgid "All Systems"
msgstr ""
#: src/components/systems-table/systems-table.tsx:261
msgid "Are you sure you want to delete {name}?"
msgstr ""
#: src/components/command-palette.tsx:186
#: src/components/navbar.tsx:102
msgid "Auth Providers"
msgstr ""
#: src/components/copy-to-clipboard.tsx:16
msgid "Automatic copy requires a secure context."
msgstr ""
#: src/components/routes/system.tsx:568
msgid "Average"
msgstr ""
#: src/components/routes/system.tsx:387
msgid "Average CPU utilization of containers"
msgstr ""
#: src/components/alerts/alerts-system.tsx:204
msgid "Average exceeds <0>{value}{0}</0>"
msgstr ""
#: src/components/routes/system.tsx:376
msgid "Average system-wide CPU utilization"
msgstr ""
#: src/components/command-palette.tsx:171
#: src/components/navbar.tsx:94
msgid "Backups"
msgstr ""
#: src/components/routes/system.tsx:436
#: src/lib/utils.ts:307
msgid "Bandwidth"
msgstr ""
#: src/components/login/auth-form.tsx:313
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
msgstr ""
#: src/components/routes/settings/notifications.tsx:127
msgid "Beszel uses <0>Shoutrrr</0> to integrate with popular notification services."
msgstr ""
#: src/components/add-system.tsx:88
msgid "Binary"
msgstr ""
#: src/components/charts/mem-chart.tsx:89
msgid "Cache / Buffers"
msgstr ""
#: src/components/systems-table/systems-table.tsx:272
msgid "Cancel"
msgstr ""
#: src/components/routes/settings/config-yaml.tsx:68
msgid "Caution - potential data loss"
msgstr ""
#: src/components/routes/settings/general.tsx:36
msgid "Change general application options."
msgstr ""
#: src/components/routes/settings/general.tsx:78
msgid "Chart options"
msgstr ""
#: src/components/login/forgot-pass-form.tsx:34
msgid "Check {email} for a reset link."
msgstr ""
#: src/components/routes/settings/layout.tsx:40
msgid "Check logs for more details."
msgstr ""
#: src/components/routes/settings/notifications.tsx:183
msgid "Check your notification service"
msgstr ""
#: src/components/add-system.tsx:153
msgid "Click to copy"
msgstr ""
#. Context: table columns
#: src/components/systems-table/systems-table.tsx:328
msgid "Columns"
msgstr ""
#: src/components/login/forgot-pass-form.tsx:83
#: src/components/login/forgot-pass-form.tsx:89
msgid "Command line instructions"
msgstr ""
#: src/components/routes/settings/notifications.tsx:77
msgid "Configure how you receive alert notifications."
msgstr ""
#: src/components/login/auth-form.tsx:189
#: src/components/login/auth-form.tsx:194
msgid "Confirm password"
msgstr ""
#: src/components/systems-table/systems-table.tsx:278
msgid "Continue"
msgstr ""
#: src/lib/utils.ts:25
msgid "Copied to clipboard"
msgstr ""
#: src/components/add-system.tsx:164
msgid "Copy"
msgstr ""
#: src/components/systems-table/systems-table.tsx:247
msgid "Copy host"
msgstr ""
#: src/components/add-system.tsx:175
msgid "Copy Linux command"
msgstr ""
#: src/components/copy-to-clipboard.tsx:13
msgid "Copy text"
msgstr ""
#: src/components/systems-table/systems-table.tsx:152
msgid "CPU"
msgstr ""
#: src/components/charts/area-chart.tsx:52
#: src/components/routes/system.tsx:375
#: src/lib/utils.ts:289
msgid "CPU Usage"
msgstr ""
#: src/components/login/auth-form.tsx:215
msgid "Create account"
msgstr ""
#. Dark theme
#: src/components/mode-toggle.tsx:21
msgid "Dark"
msgstr ""
#: src/components/command-palette.tsx:82
#: src/components/routes/home.tsx:35
msgid "Dashboard"
msgstr ""
#: src/components/routes/settings/general.tsx:85
msgid "Default time period"
msgstr ""
#: src/components/systems-table/systems-table.tsx:253
msgid "Delete"
msgstr ""
#: src/components/systems-table/systems-table.tsx:166
msgid "Disk"
msgstr ""
#: src/components/routes/system.tsx:426
msgid "Disk I/O"
msgstr ""
#: src/components/charts/disk-chart.tsx:74
#: src/components/routes/system.tsx:415
#: src/lib/utils.ts:301
msgid "Disk Usage"
msgstr ""
#: src/components/routes/system.tsx:495
msgid "Disk usage of {extraFsName}"
msgstr ""
#: src/components/routes/system.tsx:386
msgid "Docker CPU Usage"
msgstr ""
#: src/components/routes/system.tsx:407
msgid "Docker Memory Usage"
msgstr ""
#: src/components/routes/system.tsx:452
msgid "Docker Network I/O"
msgstr ""
#: src/components/command-palette.tsx:125
msgid "Documentation"
msgstr ""
#: src/components/login/auth-form.tsx:158
msgid "email"
msgstr ""
#: src/components/login/auth-form.tsx:152
#: src/components/login/forgot-pass-form.tsx:53
msgid "Email"
msgstr ""
#: src/components/routes/settings/notifications.tsx:91
msgid "Email notifications"
msgstr ""
#: src/components/login/login.tsx:36
msgid "Enter email address to reset password"
msgstr ""
#: src/components/routes/settings/notifications.tsx:111
msgid "Enter email address..."
msgstr ""
#: src/components/login/auth-form.tsx:256
#: src/components/routes/settings/config-yaml.tsx:28
#: src/components/routes/settings/notifications.tsx:187
msgid "Error"
msgstr ""
#: src/components/routes/home.tsx:81
msgid "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
msgstr ""
#: src/components/routes/settings/config-yaml.tsx:72
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
msgstr ""
#: src/components/routes/settings/config-yaml.tsx:93
msgid "Export configuration"
msgstr ""
#: src/components/routes/settings/config-yaml.tsx:48
msgid "Export your current systems configuration."
msgstr ""
#: src/lib/utils.ts:38
msgid "Failed to authenticate"
msgstr ""
#: src/components/routes/settings/layout.tsx:39
#: src/components/routes/settings/notifications.tsx:62
msgid "Failed to save settings"
msgstr ""
#: src/components/routes/settings/notifications.tsx:188
msgid "Failed to send test notification"
msgstr ""
#: src/components/alerts/alerts-system.tsx:27
msgid "Failed to update alert"
msgstr ""
#: src/components/routes/system.tsx:539
#: src/components/systems-table/systems-table.tsx:324
msgid "Filter..."
msgstr ""
#: src/components/alerts/alerts-system.tsx:225
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
msgstr ""
#: src/components/login/auth-form.tsx:337
msgid "Forgot password?"
msgstr ""
#. Context: General settings
#: src/components/routes/settings/general.tsx:33
#: src/components/routes/settings/layout.tsx:51
msgid "General"
msgstr ""
#: src/components/add-system.tsx:119
msgid "Host / IP"
msgstr ""
#: src/components/login/forgot-pass-form.tsx:93
msgid "If you've lost the password to your admin account, you may reset it using the following command."
msgstr ""
#: src/components/login/auth-form.tsx:16
msgid "Invalid email address."
msgstr ""
#. Linux kernel
#: src/components/routes/system.tsx:254
msgid "Kernel"
msgstr ""
#: src/components/routes/settings/general.tsx:45
msgid "Language"
msgstr ""
#. Light theme
#: src/components/mode-toggle.tsx:16
msgid "Light"
msgstr ""
#: src/components/navbar.tsx:113
msgid "Log Out"
msgstr ""
#: src/components/login/login.tsx:17
msgid "Login"
msgstr ""
#: src/components/login/auth-form.tsx:42
#: src/components/login/forgot-pass-form.tsx:15
msgid "Login attempt failed"
msgstr ""
#: src/components/command-palette.tsx:157
#: src/components/navbar.tsx:86
msgid "Logs"
msgstr ""
#: src/components/routes/settings/notifications.tsx:80
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
msgstr ""
#: src/components/routes/settings/layout.tsx:85
msgid "Manage display and notification preferences."
msgstr ""
#. Chart select field. Please try to keep this short.
#: src/components/routes/system.tsx:571
msgid "Max 1 min"
msgstr ""
#: src/components/systems-table/systems-table.tsx:159
msgid "Memory"
msgstr ""
#: src/components/routes/system.tsx:397
#: src/lib/utils.ts:295
msgid "Memory Usage"
msgstr ""
#: src/components/routes/system.tsx:408
msgid "Memory usage of docker containers"
msgstr ""
#: src/components/add-system.tsx:113
msgid "Name"
msgstr ""
#: src/components/systems-table/systems-table.tsx:173
msgid "Net"
msgstr ""
#: src/components/routes/system.tsx:453
msgid "Network traffic of docker containers"
msgstr ""
#: src/components/routes/system.tsx:438
msgid "Network traffic of public interfaces"
msgstr ""
#: src/components/command-palette.tsx:50
msgid "No results found."
msgstr ""
#: src/components/systems-table/systems-table.tsx:400
msgid "No systems found."
msgstr ""
#: src/components/command-palette.tsx:111
#: src/components/routes/settings/layout.tsx:56
#: src/components/routes/settings/notifications.tsx:74
msgid "Notifications"
msgstr ""
#: src/components/login/auth-form.tsx:308
msgid "OAuth 2 / OIDC support"
msgstr ""
#: src/components/routes/settings/config-yaml.tsx:61
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
msgstr ""
#: src/components/systems-table/systems-table.tsx:219
msgid "Open menu"
msgstr ""
#: src/components/login/auth-form.tsx:227
msgid "Or continue with"
msgstr ""
#: src/components/alerts/alert-button.tsx:109
msgid "Overwrite existing alerts"
msgstr ""
#: src/components/command-palette.tsx:85
msgid "Page"
msgstr ""
#: src/components/command-palette.tsx:72
msgid "Pages / Settings"
msgstr ""
#: src/components/login/auth-form.tsx:171
#: src/components/login/auth-form.tsx:176
msgid "Password"
msgstr ""
#: src/components/login/auth-form.tsx:17
msgid "Password must be at least 10 characters."
msgstr ""
#: src/components/login/forgot-pass-form.tsx:33
msgid "Password reset request received"
msgstr ""
#: src/components/systems-table/systems-table.tsx:241
msgid "Pause"
msgstr ""
#: src/components/routes/settings/notifications.tsx:95
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
msgstr ""
#: src/components/alerts/alerts-system.tsx:28
msgid "Please check logs for more details."
msgstr ""
#: src/components/login/auth-form.tsx:43
#: src/components/login/forgot-pass-form.tsx:16
msgid "Please check your credentials and try again"
msgstr ""
#: src/components/login/login.tsx:34
msgid "Please create an admin account"
msgstr ""
#: src/components/login/auth-form.tsx:257
msgid "Please enable pop-ups for this site"
msgstr ""
#: src/lib/utils.ts:39
msgid "Please log in again"
msgstr ""
#: src/components/login/auth-form.tsx:316
msgid "Please see <0>the documentation</0> for instructions."
msgstr ""
#: src/components/login/login.tsx:38
msgid "Please sign in to your account"
msgstr ""
#: src/components/add-system.tsx:125
msgid "Port"
msgstr ""
#: src/components/routes/system.tsx:398
msgid "Precise utilization at the recorded time"
msgstr ""
#: src/components/routes/settings/general.tsx:58
msgid "Preferred Language"
msgstr ""
#. Use 'Key' if your language requires many more characters
#: src/components/add-system.tsx:131
msgid "Public Key"
msgstr ""
#. Context is disk read
#: src/components/charts/area-chart.tsx:56
#: src/components/charts/area-chart.tsx:66
msgid "Read"
msgstr ""
#. Context is network bytes received (download)
#: src/components/charts/area-chart.tsx:61
msgid "Received"
msgstr ""
#: src/components/login/forgot-pass-form.tsx:76
msgid "Reset Password"
msgstr ""
#: src/components/systems-table/systems-table.tsx:236
msgid "Resume"
msgstr ""
#: src/components/routes/settings/notifications.tsx:117
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
msgstr ""
#: src/components/routes/settings/general.tsx:106
#: src/components/routes/settings/notifications.tsx:167
msgid "Save Settings"
msgstr ""
#: src/components/navbar.tsx:142
msgid "Search"
msgstr ""
#: src/components/command-palette.tsx:47
msgid "Search for systems or settings..."
msgstr ""
#: src/components/alerts/alert-button.tsx:71
msgid "See <0>notification settings</0> to configure how you receive alerts."
msgstr ""
#. Context is network bytes sent (upload)
#: src/components/charts/area-chart.tsx:60
msgid "Sent"
msgstr ""
#: src/components/routes/settings/general.tsx:100
msgid "Sets the default time range for charts when a system is viewed."
msgstr ""
#: src/components/command-palette.tsx:96
#: src/components/command-palette.tsx:99
#: src/components/command-palette.tsx:114
#: src/components/routes/settings/layout.tsx:71
#: src/components/routes/settings/layout.tsx:82
msgid "Settings"
msgstr ""
#: src/components/routes/settings/layout.tsx:33
msgid "Settings saved"
msgstr ""
#: src/components/login/auth-form.tsx:215
msgid "Sign in"
msgstr ""
#: src/components/command-palette.tsx:201
msgid "SMTP settings"
msgstr ""
#: src/lib/utils.ts:282
msgid "Status"
msgstr ""
#: src/components/routes/system.tsx:467
msgid "Swap space used by the system"
msgstr ""
#: src/components/routes/system.tsx:466
msgid "Swap Usage"
msgstr ""
#. System theme
#: src/components/mode-toggle.tsx:26
#: src/components/systems-table/systems-table.tsx:110
#: src/components/systems-table/systems-table.tsx:121
msgid "System"
msgstr ""
#: src/components/navbar.tsx:78
msgid "Systems"
msgstr ""
#: src/components/routes/settings/config-yaml.tsx:55
msgid "Systems may be managed in a <0>config.yml</0> file inside your data directory."
msgstr ""
#: src/components/routes/system.tsx:477
#: src/lib/utils.ts:314
msgid "Temperature"
msgstr ""
#: src/components/routes/system.tsx:478
msgid "Temperatures of system sensors"
msgstr ""
#: src/components/routes/settings/notifications.tsx:211
msgid "Test <0>URL</0>"
msgstr ""
#: src/components/routes/settings/notifications.tsx:182
msgid "Test notification sent"
msgstr ""
#: src/components/add-system.tsx:104
msgid "The agent must be running on the system to connect. Copy the installation command for the agent below."
msgstr ""
#: src/components/add-system.tsx:95
msgid "The agent must be running on the system to connect. Copy the<0>docker-compose.yml</0> for the agent below."
msgstr ""
#: src/components/login/forgot-pass-form.tsx:98
msgid "Then log into the backend and reset your user account password in the users table."
msgstr ""
#: src/components/systems-table/systems-table.tsx:264
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
msgstr ""
#: src/components/routes/system.tsx:507
msgid "Throughput of {extraFsName}"
msgstr ""
#: src/components/routes/system.tsx:427
msgid "Throughput of root filesystem"
msgstr ""
#: src/components/routes/settings/notifications.tsx:106
msgid "To email(s)"
msgstr ""
#: src/components/routes/system.tsx:350
#: src/components/routes/system.tsx:363
msgid "Toggle grid"
msgstr ""
#: src/components/mode-toggle.tsx:33
msgid "Toggle theme"
msgstr ""
#: src/lib/utils.ts:317
msgid "Triggers when any sensor exceeds a threshold"
msgstr ""
#: src/lib/utils.ts:310
msgid "Triggers when combined up/down exceeds a threshold"
msgstr ""
#: src/lib/utils.ts:292
msgid "Triggers when CPU usage exceeds a threshold"
msgstr ""
#: src/lib/utils.ts:298
msgid "Triggers when memory usage exceeds a threshold"
msgstr ""
#: src/lib/utils.ts:285
msgid "Triggers when status switches between up and down"
msgstr ""
#: src/lib/utils.ts:304
msgid "Triggers when usage of any disk exceeds a threshold"
msgstr ""
#: src/components/systems-table/systems-table.tsx:320
msgid "Updated in real time. Click on a system to view information."
msgstr ""
#: src/components/routes/system.tsx:253
msgid "Uptime"
msgstr ""
#: src/components/routes/system.tsx:494
msgid "Usage"
msgstr ""
#: src/components/routes/system.tsx:415
msgid "Usage of root partition"
msgstr ""
#: src/components/charts/mem-chart.tsx:65
#: src/components/charts/swap-chart.tsx:56
msgid "Used"
msgstr ""
#: src/components/login/auth-form.tsx:138
msgid "username"
msgstr ""
#: src/components/login/auth-form.tsx:131
msgid "Username"
msgstr ""
#: src/components/command-palette.tsx:143
#: src/components/navbar.tsx:70
msgid "Users"
msgstr ""
#: src/components/routes/system.tsx:603
msgid "Waiting for enough records to display"
msgstr ""
#: src/components/routes/settings/general.tsx:48
msgid "Want to help us make our translations even better? Check out <0>Crowdin</0> for more details."
msgstr ""
#: src/components/routes/settings/notifications.tsx:124
msgid "Webhook / Push notifications"
msgstr ""
#. Context is disk write
#: src/components/charts/area-chart.tsx:55
#: src/components/charts/area-chart.tsx:65
msgid "Write"
msgstr ""
#: src/components/routes/settings/layout.tsx:61
msgid "YAML Config"
msgstr ""
#: src/components/routes/settings/config-yaml.tsx:45
msgid "YAML Configuration"
msgstr ""
#: src/components/routes/settings/layout.tsx:34
msgid "Your user settings have been updated."
msgstr ""

View File

@@ -0,0 +1,808 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2024-11-01 11:30-0400\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: pt\n"
"Project-Id-Version: beszel\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-11-04 20:46\n"
"Last-Translator: \n"
"Language-Team: Portuguese\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: beszel\n"
"X-Crowdin-Project-ID: 733311\n"
"X-Crowdin-Language: pt-PT\n"
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 16\n"
#: src/components/routes/system.tsx:242
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# dia} other {# dias}}"
#: src/components/routes/system.tsx:240
msgid "{hours, plural, one {# hour} other {# hours}}"
msgstr "{hours, plural, one {# hora} other {# horas}}"
#: src/lib/utils.ts:139
msgid "1 hour"
msgstr "1 hora"
#: src/lib/utils.ts:162
msgid "1 week"
msgstr "1 semana"
#: src/lib/utils.ts:147
msgid "12 hours"
msgstr "12 horas"
#: src/lib/utils.ts:155
msgid "24 hours"
msgstr "24 horas"
#: src/lib/utils.ts:170
msgid "30 days"
msgstr "30 dias"
#. Table column
#: src/components/systems-table/systems-table.tsx:207
msgid "Actions"
msgstr "Ações"
#: src/components/routes/home.tsx:62
msgid "Active Alerts"
msgstr "Alertas Ativos"
#: src/components/add-system.tsx:74
msgid "Add <0>System</0>"
msgstr "Adicionar <0>Sistema</0>"
#: src/components/add-system.tsx:83
msgid "Add New System"
msgstr "Adicionar Novo Sistema"
#: src/components/add-system.tsx:167
#: src/components/add-system.tsx:178
msgid "Add system"
msgstr "Adicionar sistema"
#: src/components/routes/settings/notifications.tsx:156
msgid "Add URL"
msgstr "Adicionar URL"
#: src/components/routes/settings/general.tsx:81
msgid "Adjust display options for charts."
msgstr "Ajustar opções de exibição para gráficos."
#: src/components/command-palette.tsx:133
#: src/components/command-palette.tsx:146
#: src/components/command-palette.tsx:160
#: src/components/command-palette.tsx:174
#: src/components/command-palette.tsx:189
#: src/components/command-palette.tsx:204
msgid "Admin"
msgstr "Admin"
#: src/components/systems-table/systems-table.tsx:186
msgid "Agent"
msgstr "Agente"
#: src/components/alerts/alert-button.tsx:32
#: src/components/alerts/alert-button.tsx:68
msgid "Alerts"
msgstr "Alertas"
#: src/components/alerts/alert-button.tsx:88
#: src/components/systems-table/systems-table.tsx:317
msgid "All Systems"
msgstr "Todos os Sistemas"
#: src/components/systems-table/systems-table.tsx:261
msgid "Are you sure you want to delete {name}?"
msgstr "Tem certeza de que deseja excluir {name}?"
#: src/components/command-palette.tsx:186
#: src/components/navbar.tsx:102
msgid "Auth Providers"
msgstr "Provedores de Autenticação"
#: src/components/copy-to-clipboard.tsx:16
msgid "Automatic copy requires a secure context."
msgstr "A cópia automática requer um contexto seguro."
#: src/components/routes/system.tsx:568
msgid "Average"
msgstr "Média"
#: src/components/routes/system.tsx:387
msgid "Average CPU utilization of containers"
msgstr "Utilização média de CPU dos contêineres"
#: src/components/alerts/alerts-system.tsx:204
msgid "Average exceeds <0>{value}{0}</0>"
msgstr "A média excede <0>{value}{0}</0>"
#: src/components/routes/system.tsx:376
msgid "Average system-wide CPU utilization"
msgstr "Utilização média de CPU em todo o sistema"
#: src/components/command-palette.tsx:171
#: src/components/navbar.tsx:94
msgid "Backups"
msgstr "Backups"
#: src/components/routes/system.tsx:436
#: src/lib/utils.ts:307
msgid "Bandwidth"
msgstr "Largura de Banda"
#: src/components/login/auth-form.tsx:313
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
msgstr "Beszel suporta OpenID Connect e muitos provedores de autenticação OAuth2."
#: src/components/routes/settings/notifications.tsx:127
msgid "Beszel uses <0>Shoutrrr</0> to integrate with popular notification services."
msgstr "Beszel usa <0>Shoutrrr</0> para integrar com serviços de notificação populares."
#: src/components/add-system.tsx:88
msgid "Binary"
msgstr "Binário"
#: src/components/charts/mem-chart.tsx:89
msgid "Cache / Buffers"
msgstr "Cache / Buffers"
#: src/components/systems-table/systems-table.tsx:272
msgid "Cancel"
msgstr "Cancelar"
#: src/components/routes/settings/config-yaml.tsx:68
msgid "Caution - potential data loss"
msgstr "Cuidado - possível perda de dados"
#: src/components/routes/settings/general.tsx:36
msgid "Change general application options."
msgstr "Alterar opções gerais do aplicativo."
#: src/components/routes/settings/general.tsx:78
msgid "Chart options"
msgstr "Opções de gráfico"
#: src/components/login/forgot-pass-form.tsx:34
msgid "Check {email} for a reset link."
msgstr "Verifique {email} para um link de redefinição."
#: src/components/routes/settings/layout.tsx:40
msgid "Check logs for more details."
msgstr "Verifique os logs para mais detalhes."
#: src/components/routes/settings/notifications.tsx:183
msgid "Check your notification service"
msgstr "Verifique seu serviço de notificação"
#: src/components/add-system.tsx:153
msgid "Click to copy"
msgstr "Clique para copiar"
#. Context: table columns
#: src/components/systems-table/systems-table.tsx:328
msgid "Columns"
msgstr "Colunas"
#: src/components/login/forgot-pass-form.tsx:83
#: src/components/login/forgot-pass-form.tsx:89
msgid "Command line instructions"
msgstr "Instruções de linha de comando"
#: src/components/routes/settings/notifications.tsx:77
msgid "Configure how you receive alert notifications."
msgstr "Configure como você recebe notificações de alerta."
#: src/components/login/auth-form.tsx:189
#: src/components/login/auth-form.tsx:194
msgid "Confirm password"
msgstr "Confirmar senha"
#: src/components/systems-table/systems-table.tsx:278
msgid "Continue"
msgstr "Continuar"
#: src/lib/utils.ts:25
msgid "Copied to clipboard"
msgstr "Copiado para a área de transferência"
#: src/components/add-system.tsx:164
msgid "Copy"
msgstr "Copiar"
#: src/components/systems-table/systems-table.tsx:247
msgid "Copy host"
msgstr "Copiar host"
#: src/components/add-system.tsx:175
msgid "Copy Linux command"
msgstr "Copiar comando Linux"
#: src/components/copy-to-clipboard.tsx:13
msgid "Copy text"
msgstr "Copiar texto"
#: src/components/systems-table/systems-table.tsx:152
msgid "CPU"
msgstr "CPU"
#: src/components/charts/area-chart.tsx:52
#: src/components/routes/system.tsx:375
#: src/lib/utils.ts:289
msgid "CPU Usage"
msgstr "Uso de CPU"
#: src/components/login/auth-form.tsx:215
msgid "Create account"
msgstr "Criar conta"
#. Dark theme
#: src/components/mode-toggle.tsx:21
msgid "Dark"
msgstr "Escuro"
#: src/components/command-palette.tsx:82
#: src/components/routes/home.tsx:35
msgid "Dashboard"
msgstr "Painel"
#: src/components/routes/settings/general.tsx:85
msgid "Default time period"
msgstr "Período de tempo padrão"
#: src/components/systems-table/systems-table.tsx:253
msgid "Delete"
msgstr "Excluir"
#: src/components/systems-table/systems-table.tsx:166
msgid "Disk"
msgstr "Disco"
#: src/components/routes/system.tsx:426
msgid "Disk I/O"
msgstr "E/S de Disco"
#: src/components/charts/disk-chart.tsx:74
#: src/components/routes/system.tsx:415
#: src/lib/utils.ts:301
msgid "Disk Usage"
msgstr "Uso de Disco"
#: src/components/routes/system.tsx:495
msgid "Disk usage of {extraFsName}"
msgstr "Uso de disco de {extraFsName}"
#: src/components/routes/system.tsx:386
msgid "Docker CPU Usage"
msgstr "Uso de CPU do Docker"
#: src/components/routes/system.tsx:407
msgid "Docker Memory Usage"
msgstr "Uso de Memória do Docker"
#: src/components/routes/system.tsx:452
msgid "Docker Network I/O"
msgstr "E/S de Rede do Docker"
#: src/components/command-palette.tsx:125
msgid "Documentation"
msgstr "Documentação"
#: src/components/login/auth-form.tsx:158
msgid "email"
msgstr "email"
#: src/components/login/auth-form.tsx:152
#: src/components/login/forgot-pass-form.tsx:53
msgid "Email"
msgstr "Email"
#: src/components/routes/settings/notifications.tsx:91
msgid "Email notifications"
msgstr "Notificações por email"
#: src/components/login/login.tsx:36
msgid "Enter email address to reset password"
msgstr "Digite o endereço de email para redefinir a senha"
#: src/components/routes/settings/notifications.tsx:111
msgid "Enter email address..."
msgstr "Digite o endereço de email..."
#: src/components/login/auth-form.tsx:256
#: src/components/routes/settings/config-yaml.tsx:28
#: src/components/routes/settings/notifications.tsx:187
msgid "Error"
msgstr "Erro"
#: src/components/routes/home.tsx:81
msgid "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
msgstr "Excede {0}{1} no último {2, plural, one {# minuto} other {# minutos}}"
#: src/components/routes/settings/config-yaml.tsx:72
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
msgstr "Sistemas existentes não definidos em <0>config.yml</0> serão excluídos. Faça backups regulares."
#: src/components/routes/settings/config-yaml.tsx:93
msgid "Export configuration"
msgstr "Exportar configuração"
#: src/components/routes/settings/config-yaml.tsx:48
msgid "Export your current systems configuration."
msgstr "Exporte a configuração atual dos seus sistemas."
#: src/lib/utils.ts:38
msgid "Failed to authenticate"
msgstr "Falha na autenticação"
#: src/components/routes/settings/layout.tsx:39
#: src/components/routes/settings/notifications.tsx:62
msgid "Failed to save settings"
msgstr "Falha ao salvar configurações"
#: src/components/routes/settings/notifications.tsx:188
msgid "Failed to send test notification"
msgstr "Falha ao enviar notificação de teste"
#: src/components/alerts/alerts-system.tsx:27
msgid "Failed to update alert"
msgstr "Falha ao atualizar alerta"
#: src/components/routes/system.tsx:539
#: src/components/systems-table/systems-table.tsx:324
msgid "Filter..."
msgstr "Filtrar..."
#: src/components/alerts/alerts-system.tsx:225
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
msgstr "Por <0>{min}</0> {min, plural, one {minuto} other {minutos}}"
#: src/components/login/auth-form.tsx:337
msgid "Forgot password?"
msgstr "Esqueceu a senha?"
#. Context: General settings
#: src/components/routes/settings/general.tsx:33
#: src/components/routes/settings/layout.tsx:51
msgid "General"
msgstr "Geral"
#: src/components/add-system.tsx:119
msgid "Host / IP"
msgstr "Host / IP"
#: src/components/login/forgot-pass-form.tsx:93
msgid "If you've lost the password to your admin account, you may reset it using the following command."
msgstr "Se você perdeu a senha da sua conta de administrador, pode redefini-la usando o seguinte comando."
#: src/components/login/auth-form.tsx:16
msgid "Invalid email address."
msgstr "Endereço de email inválido."
#. Linux kernel
#: src/components/routes/system.tsx:254
msgid "Kernel"
msgstr "Kernel"
#: src/components/routes/settings/general.tsx:45
msgid "Language"
msgstr "Idioma"
#. Light theme
#: src/components/mode-toggle.tsx:16
msgid "Light"
msgstr "Claro"
#: src/components/navbar.tsx:113
msgid "Log Out"
msgstr "Sair"
#: src/components/login/login.tsx:17
msgid "Login"
msgstr "Entrar"
#: src/components/login/auth-form.tsx:42
#: src/components/login/forgot-pass-form.tsx:15
msgid "Login attempt failed"
msgstr "Tentativa de login falhou"
#: src/components/command-palette.tsx:157
#: src/components/navbar.tsx:86
msgid "Logs"
msgstr "Logs"
#: src/components/routes/settings/notifications.tsx:80
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
msgstr "Procurando onde criar alertas? Clique nos ícones de sino <0/> na tabela de sistemas."
#: src/components/routes/settings/layout.tsx:85
msgid "Manage display and notification preferences."
msgstr "Gerenciar preferências de exibição e notificação."
#. Chart select field. Please try to keep this short.
#: src/components/routes/system.tsx:571
msgid "Max 1 min"
msgstr "Máx 1 min"
#: src/components/systems-table/systems-table.tsx:159
msgid "Memory"
msgstr "Memória"
#: src/components/routes/system.tsx:397
#: src/lib/utils.ts:295
msgid "Memory Usage"
msgstr "Uso de Memória"
#: src/components/routes/system.tsx:408
msgid "Memory usage of docker containers"
msgstr "Uso de memória dos contêineres Docker"
#: src/components/add-system.tsx:113
msgid "Name"
msgstr "Nome"
#: src/components/systems-table/systems-table.tsx:173
msgid "Net"
msgstr "Rede"
#: src/components/routes/system.tsx:453
msgid "Network traffic of docker containers"
msgstr "Tráfego de rede dos contêineres Docker"
#: src/components/routes/system.tsx:438
msgid "Network traffic of public interfaces"
msgstr "Tráfego de rede das interfaces públicas"
#: src/components/command-palette.tsx:50
msgid "No results found."
msgstr "Nenhum resultado encontrado."
#: src/components/systems-table/systems-table.tsx:400
msgid "No systems found."
msgstr "Nenhum sistema encontrado."
#: src/components/command-palette.tsx:111
#: src/components/routes/settings/layout.tsx:56
#: src/components/routes/settings/notifications.tsx:74
msgid "Notifications"
msgstr "Notificações"
#: src/components/login/auth-form.tsx:308
msgid "OAuth 2 / OIDC support"
msgstr "Suporte a OAuth 2 / OIDC"
#: src/components/routes/settings/config-yaml.tsx:61
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
msgstr "A cada reinício, os sistemas no banco de dados serão atualizados para corresponder aos sistemas definidos no arquivo."
#: src/components/systems-table/systems-table.tsx:219
msgid "Open menu"
msgstr "Abrir menu"
#: src/components/login/auth-form.tsx:227
msgid "Or continue with"
msgstr "Ou continue com"
#: src/components/alerts/alert-button.tsx:109
msgid "Overwrite existing alerts"
msgstr "Sobrescrever alertas existentes"
#: src/components/command-palette.tsx:85
msgid "Page"
msgstr "Página"
#: src/components/command-palette.tsx:72
msgid "Pages / Settings"
msgstr "Páginas / Configurações"
#: src/components/login/auth-form.tsx:171
#: src/components/login/auth-form.tsx:176
msgid "Password"
msgstr "Senha"
#: src/components/login/auth-form.tsx:17
msgid "Password must be at least 10 characters."
msgstr "A senha deve ter pelo menos 10 caracteres."
#: src/components/login/forgot-pass-form.tsx:33
msgid "Password reset request received"
msgstr "Solicitação de redefinição de senha recebida"
#: src/components/systems-table/systems-table.tsx:241
msgid "Pause"
msgstr "Pausar"
#: src/components/routes/settings/notifications.tsx:95
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
msgstr "Por favor, <0>configure um servidor SMTP</0> para garantir que os alertas sejam entregues."
#: src/components/alerts/alerts-system.tsx:28
msgid "Please check logs for more details."
msgstr "Por favor, verifique os logs para mais detalhes."
#: src/components/login/auth-form.tsx:43
#: src/components/login/forgot-pass-form.tsx:16
msgid "Please check your credentials and try again"
msgstr "Por favor, verifique suas credenciais e tente novamente"
#: src/components/login/login.tsx:34
msgid "Please create an admin account"
msgstr "Por favor, crie uma conta de administrador"
#: src/components/login/auth-form.tsx:257
msgid "Please enable pop-ups for this site"
msgstr "Por favor, habilite pop-ups para este site"
#: src/lib/utils.ts:39
msgid "Please log in again"
msgstr "Por favor, faça login novamente"
#: src/components/login/auth-form.tsx:316
msgid "Please see <0>the documentation</0> for instructions."
msgstr "Por favor, veja <0>a documentação</0> para instruções."
#: src/components/login/login.tsx:38
msgid "Please sign in to your account"
msgstr "Por favor, entre na sua conta"
#: src/components/add-system.tsx:125
msgid "Port"
msgstr "Porta"
#: src/components/routes/system.tsx:398
msgid "Precise utilization at the recorded time"
msgstr "Utilização precisa no momento registrado"
#: src/components/routes/settings/general.tsx:58
msgid "Preferred Language"
msgstr "Idioma Preferido"
#. Use 'Key' if your language requires many more characters
#: src/components/add-system.tsx:131
msgid "Public Key"
msgstr "Chave Pública"
#. Context is disk read
#: src/components/charts/area-chart.tsx:56
#: src/components/charts/area-chart.tsx:66
msgid "Read"
msgstr "Ler"
#. Context is network bytes received (download)
#: src/components/charts/area-chart.tsx:61
msgid "Received"
msgstr "Recebido"
#: src/components/login/forgot-pass-form.tsx:76
msgid "Reset Password"
msgstr "Redefinir Senha"
#: src/components/systems-table/systems-table.tsx:236
msgid "Resume"
msgstr "Retomar"
#: src/components/routes/settings/notifications.tsx:117
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
msgstr "Salve o endereço usando a tecla enter ou vírgula. Deixe em branco para desativar notificações por email."
#: src/components/routes/settings/general.tsx:106
#: src/components/routes/settings/notifications.tsx:167
msgid "Save Settings"
msgstr "Salvar Configurações"
#: src/components/navbar.tsx:142
msgid "Search"
msgstr "Pesquisar"
#: src/components/command-palette.tsx:47
msgid "Search for systems or settings..."
msgstr "Pesquisar por sistemas ou configurações..."
#: src/components/alerts/alert-button.tsx:71
msgid "See <0>notification settings</0> to configure how you receive alerts."
msgstr "Veja <0>configurações de notificação</0> para configurar como você recebe alertas."
#. Context is network bytes sent (upload)
#: src/components/charts/area-chart.tsx:60
msgid "Sent"
msgstr "Enviado"
#: src/components/routes/settings/general.tsx:100
msgid "Sets the default time range for charts when a system is viewed."
msgstr "Define o intervalo de tempo padrão para gráficos quando um sistema é visualizado."
#: src/components/command-palette.tsx:96
#: src/components/command-palette.tsx:99
#: src/components/command-palette.tsx:114
#: src/components/routes/settings/layout.tsx:71
#: src/components/routes/settings/layout.tsx:82
msgid "Settings"
msgstr "Configurações"
#: src/components/routes/settings/layout.tsx:33
msgid "Settings saved"
msgstr "Configurações salvas"
#: src/components/login/auth-form.tsx:215
msgid "Sign in"
msgstr "Entrar"
#: src/components/command-palette.tsx:201
msgid "SMTP settings"
msgstr "Configurações SMTP"
#: src/lib/utils.ts:282
msgid "Status"
msgstr "Status"
#: src/components/routes/system.tsx:467
msgid "Swap space used by the system"
msgstr "Espaço de swap usado pelo sistema"
#: src/components/routes/system.tsx:466
msgid "Swap Usage"
msgstr "Uso de Swap"
#. System theme
#: src/components/mode-toggle.tsx:26
#: src/components/systems-table/systems-table.tsx:110
#: src/components/systems-table/systems-table.tsx:121
msgid "System"
msgstr "Sistema"
#: src/components/navbar.tsx:78
msgid "Systems"
msgstr "Sistemas"
#: src/components/routes/settings/config-yaml.tsx:55
msgid "Systems may be managed in a <0>config.yml</0> file inside your data directory."
msgstr "Os sistemas podem ser gerenciados em um arquivo <0>config.yml</0> dentro do seu diretório de dados."
#: src/components/routes/system.tsx:477
#: src/lib/utils.ts:314
msgid "Temperature"
msgstr "Temperatura"
#: src/components/routes/system.tsx:478
msgid "Temperatures of system sensors"
msgstr "Temperaturas dos sensores do sistema"
#: src/components/routes/settings/notifications.tsx:211
msgid "Test <0>URL</0>"
msgstr "Testar <0>URL</0>"
#: src/components/routes/settings/notifications.tsx:182
msgid "Test notification sent"
msgstr "Notificação de teste enviada"
#: src/components/add-system.tsx:104
msgid "The agent must be running on the system to connect. Copy the installation command for the agent below."
msgstr "O agente deve estar em execução no sistema para conectar. Copie o comando de instalação para o agente abaixo."
#: src/components/add-system.tsx:95
msgid "The agent must be running on the system to connect. Copy the<0>docker-compose.yml</0> for the agent below."
msgstr "O agente deve estar em execução no sistema para conectar. Copie o <0>docker-compose.yml</0> para o agente abaixo."
#: src/components/login/forgot-pass-form.tsx:98
msgid "Then log into the backend and reset your user account password in the users table."
msgstr "Em seguida, faça login no backend e redefina a senha da sua conta de usuário na tabela de usuários."
#: src/components/systems-table/systems-table.tsx:264
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
msgstr "Esta ação não pode ser desfeita. Isso excluirá permanentemente todos os registros atuais de {name} do banco de dados."
#: src/components/routes/system.tsx:507
msgid "Throughput of {extraFsName}"
msgstr "Taxa de transferência de {extraFsName}"
#: src/components/routes/system.tsx:427
msgid "Throughput of root filesystem"
msgstr "Taxa de transferência do sistema de arquivos raiz"
#: src/components/routes/settings/notifications.tsx:106
msgid "To email(s)"
msgstr "Para email(s)"
#: src/components/routes/system.tsx:350
#: src/components/routes/system.tsx:363
msgid "Toggle grid"
msgstr "Alternar grade"
#: src/components/mode-toggle.tsx:33
msgid "Toggle theme"
msgstr "Alternar tema"
#: src/lib/utils.ts:317
msgid "Triggers when any sensor exceeds a threshold"
msgstr "Dispara quando qualquer sensor excede um limite"
#: src/lib/utils.ts:310
msgid "Triggers when combined up/down exceeds a threshold"
msgstr "Dispara quando a soma de subida/descida excede um limite"
#: src/lib/utils.ts:292
msgid "Triggers when CPU usage exceeds a threshold"
msgstr "Dispara quando o uso de CPU excede um limite"
#: src/lib/utils.ts:298
msgid "Triggers when memory usage exceeds a threshold"
msgstr "Dispara quando o uso de memória excede um limite"
#: src/lib/utils.ts:285
msgid "Triggers when status switches between up and down"
msgstr "Dispara quando o status alterna entre ativo e inativo"
#: src/lib/utils.ts:304
msgid "Triggers when usage of any disk exceeds a threshold"
msgstr "Dispara quando o uso de qualquer disco excede um limite"
#: src/components/systems-table/systems-table.tsx:320
msgid "Updated in real time. Click on a system to view information."
msgstr "Atualizado em tempo real. Clique em um sistema para ver informações."
#: src/components/routes/system.tsx:253
msgid "Uptime"
msgstr "Tempo de Atividade"
#: src/components/routes/system.tsx:494
msgid "Usage"
msgstr "Uso"
#: src/components/routes/system.tsx:415
msgid "Usage of root partition"
msgstr "Uso da partição raiz"
#: src/components/charts/mem-chart.tsx:65
#: src/components/charts/swap-chart.tsx:56
msgid "Used"
msgstr "Usado"
#: src/components/login/auth-form.tsx:138
msgid "username"
msgstr "nome de usuário"
#: src/components/login/auth-form.tsx:131
msgid "Username"
msgstr "Nome de usuário"
#: src/components/command-palette.tsx:143
#: src/components/navbar.tsx:70
msgid "Users"
msgstr "Usuários"
#: src/components/routes/system.tsx:603
msgid "Waiting for enough records to display"
msgstr "Aguardando registros suficientes para exibir"
#: src/components/routes/settings/general.tsx:48
msgid "Want to help us make our translations even better? Check out <0>Crowdin</0> for more details."
msgstr "Quer nos ajudar a melhorar ainda mais nossas traduções? Confira <0>Crowdin</0> para mais detalhes."
#: src/components/routes/settings/notifications.tsx:124
msgid "Webhook / Push notifications"
msgstr "Notificações Webhook / Push"
#. Context is disk write
#: src/components/charts/area-chart.tsx:55
#: src/components/charts/area-chart.tsx:65
msgid "Write"
msgstr "Escrever"
#: src/components/routes/settings/layout.tsx:61
msgid "YAML Config"
msgstr "Configuração YAML"
#: src/components/routes/settings/config-yaml.tsx:45
msgid "YAML Configuration"
msgstr "Configuração YAML"
#: src/components/routes/settings/layout.tsx:34
msgid "Your user settings have been updated."
msgstr "As configurações do seu usuário foram atualizadas."

View File

@@ -0,0 +1,808 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2024-11-01 11:30-0400\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: ru\n"
"Project-Id-Version: beszel\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-11-04 20:46\n"
"Last-Translator: \n"
"Language-Team: Russian\n"
"Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n"
"X-Crowdin-Project: beszel\n"
"X-Crowdin-Project-ID: 733311\n"
"X-Crowdin-Language: ru\n"
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 16\n"
#: src/components/routes/system.tsx:242
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# день} other {# дней}}"
#: src/components/routes/system.tsx:240
msgid "{hours, plural, one {# hour} other {# hours}}"
msgstr "{hours, plural, one {# час} other {# часов}}"
#: src/lib/utils.ts:139
msgid "1 hour"
msgstr "1 час"
#: src/lib/utils.ts:162
msgid "1 week"
msgstr "1 неделя"
#: src/lib/utils.ts:147
msgid "12 hours"
msgstr "12 часов"
#: src/lib/utils.ts:155
msgid "24 hours"
msgstr "24 часа"
#: src/lib/utils.ts:170
msgid "30 days"
msgstr "30 дней"
#. Table column
#: src/components/systems-table/systems-table.tsx:207
msgid "Actions"
msgstr "Действия"
#: src/components/routes/home.tsx:62
msgid "Active Alerts"
msgstr "Активные оповещения"
#: src/components/add-system.tsx:74
msgid "Add <0>System</0>"
msgstr "Добавить <0>Систему</0>"
#: src/components/add-system.tsx:83
msgid "Add New System"
msgstr "Добавить новую систему"
#: src/components/add-system.tsx:167
#: src/components/add-system.tsx:178
msgid "Add system"
msgstr "Добавить систему"
#: src/components/routes/settings/notifications.tsx:156
msgid "Add URL"
msgstr "Добавить URL"
#: src/components/routes/settings/general.tsx:81
msgid "Adjust display options for charts."
msgstr "Настроить параметры отображения для графиков."
#: src/components/command-palette.tsx:133
#: src/components/command-palette.tsx:146
#: src/components/command-palette.tsx:160
#: src/components/command-palette.tsx:174
#: src/components/command-palette.tsx:189
#: src/components/command-palette.tsx:204
msgid "Admin"
msgstr "Администратор"
#: src/components/systems-table/systems-table.tsx:186
msgid "Agent"
msgstr "Агент"
#: src/components/alerts/alert-button.tsx:32
#: src/components/alerts/alert-button.tsx:68
msgid "Alerts"
msgstr "Оповещения"
#: src/components/alerts/alert-button.tsx:88
#: src/components/systems-table/systems-table.tsx:317
msgid "All Systems"
msgstr "Все системы"
#: src/components/systems-table/systems-table.tsx:261
msgid "Are you sure you want to delete {name}?"
msgstr "Вы уверены, что хотите удалить {name}?"
#: src/components/command-palette.tsx:186
#: src/components/navbar.tsx:102
msgid "Auth Providers"
msgstr "Поставщики аутентификации"
#: src/components/copy-to-clipboard.tsx:16
msgid "Automatic copy requires a secure context."
msgstr "Автоматическое копирование требует безопасного контекста."
#: src/components/routes/system.tsx:568
msgid "Average"
msgstr "Среднее"
#: src/components/routes/system.tsx:387
msgid "Average CPU utilization of containers"
msgstr "Среднее использование CPU контейнерами"
#: src/components/alerts/alerts-system.tsx:204
msgid "Average exceeds <0>{value}{0}</0>"
msgstr "Среднее превышает <0>{value}{0}</0>"
#: src/components/routes/system.tsx:376
msgid "Average system-wide CPU utilization"
msgstr "Среднее использование CPU по всей системе"
#: src/components/command-palette.tsx:171
#: src/components/navbar.tsx:94
msgid "Backups"
msgstr "Резервные копии"
#: src/components/routes/system.tsx:436
#: src/lib/utils.ts:307
msgid "Bandwidth"
msgstr "Пропускная способность"
#: src/components/login/auth-form.tsx:313
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
msgstr "Beszel поддерживает OpenID Connect и многих поставщиков аутентификации OAuth2."
#: src/components/routes/settings/notifications.tsx:127
msgid "Beszel uses <0>Shoutrrr</0> to integrate with popular notification services."
msgstr "Beszel использует <0>Shoutrrr</0> для интеграции с популярными сервисами уведомлений."
#: src/components/add-system.tsx:88
msgid "Binary"
msgstr "Двоичный"
#: src/components/charts/mem-chart.tsx:89
msgid "Cache / Buffers"
msgstr "Кэш / Буферы"
#: src/components/systems-table/systems-table.tsx:272
msgid "Cancel"
msgstr "Отмена"
#: src/components/routes/settings/config-yaml.tsx:68
msgid "Caution - potential data loss"
msgstr "Внимание - возможная потеря данных"
#: src/components/routes/settings/general.tsx:36
msgid "Change general application options."
msgstr "Изменить общие параметры приложения."
#: src/components/routes/settings/general.tsx:78
msgid "Chart options"
msgstr "Параметры графиков"
#: src/components/login/forgot-pass-form.tsx:34
msgid "Check {email} for a reset link."
msgstr "Проверьте {email} для получения ссылки на сброс."
#: src/components/routes/settings/layout.tsx:40
msgid "Check logs for more details."
msgstr "Проверьте журналы для получения более подробной информации."
#: src/components/routes/settings/notifications.tsx:183
msgid "Check your notification service"
msgstr "Проверьте ваш сервис уведомлений"
#: src/components/add-system.tsx:153
msgid "Click to copy"
msgstr "Нажмите, чтобы скопировать"
#. Context: table columns
#: src/components/systems-table/systems-table.tsx:328
msgid "Columns"
msgstr "Столбцы"
#: src/components/login/forgot-pass-form.tsx:83
#: src/components/login/forgot-pass-form.tsx:89
msgid "Command line instructions"
msgstr "Инструкции командной строки"
#: src/components/routes/settings/notifications.tsx:77
msgid "Configure how you receive alert notifications."
msgstr "Настройте, как вы получаете уведомления об оповещениях."
#: src/components/login/auth-form.tsx:189
#: src/components/login/auth-form.tsx:194
msgid "Confirm password"
msgstr "Подтвердите пароль"
#: src/components/systems-table/systems-table.tsx:278
msgid "Continue"
msgstr "Продолжить"
#: src/lib/utils.ts:25
msgid "Copied to clipboard"
msgstr "Скопировано в буфер обмена"
#: src/components/add-system.tsx:164
msgid "Copy"
msgstr "Копировать"
#: src/components/systems-table/systems-table.tsx:247
msgid "Copy host"
msgstr "Копировать хост"
#: src/components/add-system.tsx:175
msgid "Copy Linux command"
msgstr "Копировать команду Linux"
#: src/components/copy-to-clipboard.tsx:13
msgid "Copy text"
msgstr "Копировать текст"
#: src/components/systems-table/systems-table.tsx:152
msgid "CPU"
msgstr "CPU"
#: src/components/charts/area-chart.tsx:52
#: src/components/routes/system.tsx:375
#: src/lib/utils.ts:289
msgid "CPU Usage"
msgstr "Использование CPU"
#: src/components/login/auth-form.tsx:215
msgid "Create account"
msgstr "Создать аккаунт"
#. Dark theme
#: src/components/mode-toggle.tsx:21
msgid "Dark"
msgstr "Темная"
#: src/components/command-palette.tsx:82
#: src/components/routes/home.tsx:35
msgid "Dashboard"
msgstr "Панель управления"
#: src/components/routes/settings/general.tsx:85
msgid "Default time period"
msgstr "Период по умолчанию"
#: src/components/systems-table/systems-table.tsx:253
msgid "Delete"
msgstr "Удалить"
#: src/components/systems-table/systems-table.tsx:166
msgid "Disk"
msgstr "Диск"
#: src/components/routes/system.tsx:426
msgid "Disk I/O"
msgstr "Дисковый ввод/вывод"
#: src/components/charts/disk-chart.tsx:74
#: src/components/routes/system.tsx:415
#: src/lib/utils.ts:301
msgid "Disk Usage"
msgstr "Использование диска"
#: src/components/routes/system.tsx:495
msgid "Disk usage of {extraFsName}"
msgstr "Использование диска {extraFsName}"
#: src/components/routes/system.tsx:386
msgid "Docker CPU Usage"
msgstr "Использование CPU Docker"
#: src/components/routes/system.tsx:407
msgid "Docker Memory Usage"
msgstr "Использование памяти Docker"
#: src/components/routes/system.tsx:452
msgid "Docker Network I/O"
msgstr "Сетевой ввод/вывод Docker"
#: src/components/command-palette.tsx:125
msgid "Documentation"
msgstr "Документация"
#: src/components/login/auth-form.tsx:158
msgid "email"
msgstr "электронная почта"
#: src/components/login/auth-form.tsx:152
#: src/components/login/forgot-pass-form.tsx:53
msgid "Email"
msgstr "Электронная почта"
#: src/components/routes/settings/notifications.tsx:91
msgid "Email notifications"
msgstr "Уведомления по электронной почте"
#: src/components/login/login.tsx:36
msgid "Enter email address to reset password"
msgstr "Введите адрес электронной почты для сброса пароля"
#: src/components/routes/settings/notifications.tsx:111
msgid "Enter email address..."
msgstr "Введите адрес электронной почты..."
#: src/components/login/auth-form.tsx:256
#: src/components/routes/settings/config-yaml.tsx:28
#: src/components/routes/settings/notifications.tsx:187
msgid "Error"
msgstr "Ошибка"
#: src/components/routes/home.tsx:81
msgid "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
msgstr "Превышает {0}{1} за последние {2, plural, one {# минуту} other {# минут}}"
#: src/components/routes/settings/config-yaml.tsx:72
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
msgstr "Существующие системы, не определенные в <0>config.yml</0>, будут удалены. Пожалуйста, делайте регулярные резервные копии."
#: src/components/routes/settings/config-yaml.tsx:93
msgid "Export configuration"
msgstr "Экспорт конфигурации"
#: src/components/routes/settings/config-yaml.tsx:48
msgid "Export your current systems configuration."
msgstr "Экспортируйте текущую конфигурацию систем."
#: src/lib/utils.ts:38
msgid "Failed to authenticate"
msgstr "Не удалось аутентифицировать"
#: src/components/routes/settings/layout.tsx:39
#: src/components/routes/settings/notifications.tsx:62
msgid "Failed to save settings"
msgstr "Не удалось сохранить настройки"
#: src/components/routes/settings/notifications.tsx:188
msgid "Failed to send test notification"
msgstr "Не удалось отправить тестовое уведомление"
#: src/components/alerts/alerts-system.tsx:27
msgid "Failed to update alert"
msgstr "Не удалось обновить оповещение"
#: src/components/routes/system.tsx:539
#: src/components/systems-table/systems-table.tsx:324
msgid "Filter..."
msgstr "Фильтр..."
#: src/components/alerts/alerts-system.tsx:225
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
msgstr "На <0>{min}</0> {min, plural, one {минуту} other {минут}}"
#: src/components/login/auth-form.tsx:337
msgid "Forgot password?"
msgstr "Забыли пароль?"
#. Context: General settings
#: src/components/routes/settings/general.tsx:33
#: src/components/routes/settings/layout.tsx:51
msgid "General"
msgstr "Общие"
#: src/components/add-system.tsx:119
msgid "Host / IP"
msgstr "Хост / IP"
#: src/components/login/forgot-pass-form.tsx:93
msgid "If you've lost the password to your admin account, you may reset it using the following command."
msgstr "Если вы потеряли пароль от своей учетной записи администратора, вы можете сбросить его, используя следующую команду."
#: src/components/login/auth-form.tsx:16
msgid "Invalid email address."
msgstr "Неверный адрес электронной почты."
#. Linux kernel
#: src/components/routes/system.tsx:254
msgid "Kernel"
msgstr "Ядро"
#: src/components/routes/settings/general.tsx:45
msgid "Language"
msgstr "Язык"
#. Light theme
#: src/components/mode-toggle.tsx:16
msgid "Light"
msgstr "Светлая"
#: src/components/navbar.tsx:113
msgid "Log Out"
msgstr "Выйти"
#: src/components/login/login.tsx:17
msgid "Login"
msgstr "Вход"
#: src/components/login/auth-form.tsx:42
#: src/components/login/forgot-pass-form.tsx:15
msgid "Login attempt failed"
msgstr "Попытка входа не удалась"
#: src/components/command-palette.tsx:157
#: src/components/navbar.tsx:86
msgid "Logs"
msgstr "Журналы"
#: src/components/routes/settings/notifications.tsx:80
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
msgstr "Ищете, где создать оповещения? Нажмите на значки колокольчика <0/> в таблице систем."
#: src/components/routes/settings/layout.tsx:85
msgid "Manage display and notification preferences."
msgstr "Управляйте предпочтениями отображения и уведомлений."
#. Chart select field. Please try to keep this short.
#: src/components/routes/system.tsx:571
msgid "Max 1 min"
msgstr "Макс 1 мин"
#: src/components/systems-table/systems-table.tsx:159
msgid "Memory"
msgstr "Память"
#: src/components/routes/system.tsx:397
#: src/lib/utils.ts:295
msgid "Memory Usage"
msgstr "Использование памяти"
#: src/components/routes/system.tsx:408
msgid "Memory usage of docker containers"
msgstr "Использование памяти контейнерами Docker"
#: src/components/add-system.tsx:113
msgid "Name"
msgstr "Имя"
#: src/components/systems-table/systems-table.tsx:173
msgid "Net"
msgstr "Сеть"
#: src/components/routes/system.tsx:453
msgid "Network traffic of docker containers"
msgstr "Сетевой трафик контейнеров Docker"
#: src/components/routes/system.tsx:438
msgid "Network traffic of public interfaces"
msgstr "Сетевой трафик публичных интерфейсов"
#: src/components/command-palette.tsx:50
msgid "No results found."
msgstr "Результаты не найдены."
#: src/components/systems-table/systems-table.tsx:400
msgid "No systems found."
msgstr "Системы не найдены."
#: src/components/command-palette.tsx:111
#: src/components/routes/settings/layout.tsx:56
#: src/components/routes/settings/notifications.tsx:74
msgid "Notifications"
msgstr "Уведомления"
#: src/components/login/auth-form.tsx:308
msgid "OAuth 2 / OIDC support"
msgstr "Поддержка OAuth 2 / OIDC"
#: src/components/routes/settings/config-yaml.tsx:61
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
msgstr "При каждом перезапуске системы в базе данных будут обновлены в соответствии с системами, определенными в файле."
#: src/components/systems-table/systems-table.tsx:219
msgid "Open menu"
msgstr "Открыть меню"
#: src/components/login/auth-form.tsx:227
msgid "Or continue with"
msgstr "Или продолжить с"
#: src/components/alerts/alert-button.tsx:109
msgid "Overwrite existing alerts"
msgstr "Перезаписать существующие оповещения"
#: src/components/command-palette.tsx:85
msgid "Page"
msgstr "Страница"
#: src/components/command-palette.tsx:72
msgid "Pages / Settings"
msgstr "Страницы / Настройки"
#: src/components/login/auth-form.tsx:171
#: src/components/login/auth-form.tsx:176
msgid "Password"
msgstr "Пароль"
#: src/components/login/auth-form.tsx:17
msgid "Password must be at least 10 characters."
msgstr "Пароль должен содержать не менее 10 символов."
#: src/components/login/forgot-pass-form.tsx:33
msgid "Password reset request received"
msgstr "Запрос на сброс пароля получен"
#: src/components/systems-table/systems-table.tsx:241
msgid "Pause"
msgstr "Пауза"
#: src/components/routes/settings/notifications.tsx:95
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
msgstr "Пожалуйста, <0>настройте SMTP-сервер</0>, чтобы гарантировать доставку оповещений."
#: src/components/alerts/alerts-system.tsx:28
msgid "Please check logs for more details."
msgstr "Пожалуйста, проверьте журналы для получения более подробной информации."
#: src/components/login/auth-form.tsx:43
#: src/components/login/forgot-pass-form.tsx:16
msgid "Please check your credentials and try again"
msgstr "Пожалуйста, проверьте свои учетные данные и попробуйте снова"
#: src/components/login/login.tsx:34
msgid "Please create an admin account"
msgstr "Пожалуйста, создайте учетную запись администратора"
#: src/components/login/auth-form.tsx:257
msgid "Please enable pop-ups for this site"
msgstr "Пожалуйста, включите всплывающие окна для этого сайта"
#: src/lib/utils.ts:39
msgid "Please log in again"
msgstr "Пожалуйста, войдите снова"
#: src/components/login/auth-form.tsx:316
msgid "Please see <0>the documentation</0> for instructions."
msgstr "Пожалуйста, смотрите <0>документацию</0> для получения инструкций."
#: src/components/login/login.tsx:38
msgid "Please sign in to your account"
msgstr "Пожалуйста, войдите в свою учетную запись"
#: src/components/add-system.tsx:125
msgid "Port"
msgstr "Порт"
#: src/components/routes/system.tsx:398
msgid "Precise utilization at the recorded time"
msgstr "Точное использование в записанное время"
#: src/components/routes/settings/general.tsx:58
msgid "Preferred Language"
msgstr "Предпочтительный язык"
#. Use 'Key' if your language requires many more characters
#: src/components/add-system.tsx:131
msgid "Public Key"
msgstr "Ключ"
#. Context is disk read
#: src/components/charts/area-chart.tsx:56
#: src/components/charts/area-chart.tsx:66
msgid "Read"
msgstr "Чтение"
#. Context is network bytes received (download)
#: src/components/charts/area-chart.tsx:61
msgid "Received"
msgstr "Получено"
#: src/components/login/forgot-pass-form.tsx:76
msgid "Reset Password"
msgstr "Сбросить пароль"
#: src/components/systems-table/systems-table.tsx:236
msgid "Resume"
msgstr "Возобновить"
#: src/components/routes/settings/notifications.tsx:117
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
msgstr "Сохраните адрес, используя клавишу ввода или запятую. Оставьте пустым, чтобы отключить уведомления по электронной почте."
#: src/components/routes/settings/general.tsx:106
#: src/components/routes/settings/notifications.tsx:167
msgid "Save Settings"
msgstr "Сохранить настройки"
#: src/components/navbar.tsx:142
msgid "Search"
msgstr "Поиск"
#: src/components/command-palette.tsx:47
msgid "Search for systems or settings..."
msgstr "Поиск систем или настроек..."
#: src/components/alerts/alert-button.tsx:71
msgid "See <0>notification settings</0> to configure how you receive alerts."
msgstr "Смотрите <0>настройки уведомлений</0>, чтобы настроить, как вы получаете оповещения."
#. Context is network bytes sent (upload)
#: src/components/charts/area-chart.tsx:60
msgid "Sent"
msgstr "Отправлено"
#: src/components/routes/settings/general.tsx:100
msgid "Sets the default time range for charts when a system is viewed."
msgstr "Устанавливает диапазон времени по умолчанию для графиков при просмотре системы."
#: src/components/command-palette.tsx:96
#: src/components/command-palette.tsx:99
#: src/components/command-palette.tsx:114
#: src/components/routes/settings/layout.tsx:71
#: src/components/routes/settings/layout.tsx:82
msgid "Settings"
msgstr "Настройки"
#: src/components/routes/settings/layout.tsx:33
msgid "Settings saved"
msgstr "Настройки сохранены"
#: src/components/login/auth-form.tsx:215
msgid "Sign in"
msgstr "Войти"
#: src/components/command-palette.tsx:201
msgid "SMTP settings"
msgstr "Настройки SMTP"
#: src/lib/utils.ts:282
msgid "Status"
msgstr "Статус"
#: src/components/routes/system.tsx:467
msgid "Swap space used by the system"
msgstr "Используемое системой пространство подкачки"
#: src/components/routes/system.tsx:466
msgid "Swap Usage"
msgstr "Использование подкачки"
#. System theme
#: src/components/mode-toggle.tsx:26
#: src/components/systems-table/systems-table.tsx:110
#: src/components/systems-table/systems-table.tsx:121
msgid "System"
msgstr "Система"
#: src/components/navbar.tsx:78
msgid "Systems"
msgstr "Системы"
#: src/components/routes/settings/config-yaml.tsx:55
msgid "Systems may be managed in a <0>config.yml</0> file inside your data directory."
msgstr "Системы могут управляться в файле <0>config.yml</0> внутри вашего каталога данных."
#: src/components/routes/system.tsx:477
#: src/lib/utils.ts:314
msgid "Temperature"
msgstr "Температура"
#: src/components/routes/system.tsx:478
msgid "Temperatures of system sensors"
msgstr "Температуры датчиков системы"
#: src/components/routes/settings/notifications.tsx:211
msgid "Test <0>URL</0>"
msgstr "Тест <0>URL</0>"
#: src/components/routes/settings/notifications.tsx:182
msgid "Test notification sent"
msgstr "Тестовое уведомление отправлено"
#: src/components/add-system.tsx:104
msgid "The agent must be running on the system to connect. Copy the installation command for the agent below."
msgstr "Агент должен работать на системе для подключения. Скопируйте команду установки агента ниже."
#: src/components/add-system.tsx:95
msgid "The agent must be running on the system to connect. Copy the<0>docker-compose.yml</0> for the agent below."
msgstr "Агент должен работать на системе для подключения. Скопируйте <0>docker-compose.yml</0> для агента ниже."
#: src/components/login/forgot-pass-form.tsx:98
msgid "Then log into the backend and reset your user account password in the users table."
msgstr "Затем войдите в бэкенд и сбросьте пароль вашей учетной записи в таблице пользователей."
#: src/components/systems-table/systems-table.tsx:264
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
msgstr "Это действие не может быть отменено. Это навсегда удалит все текущие записи для {name} из базы данных."
#: src/components/routes/system.tsx:507
msgid "Throughput of {extraFsName}"
msgstr "Пропускная способность {extraFsName}"
#: src/components/routes/system.tsx:427
msgid "Throughput of root filesystem"
msgstr "Пропускная способность корневой файловой системы"
#: src/components/routes/settings/notifications.tsx:106
msgid "To email(s)"
msgstr "На электронную почту"
#: src/components/routes/system.tsx:350
#: src/components/routes/system.tsx:363
msgid "Toggle grid"
msgstr "Переключить сетку"
#: src/components/mode-toggle.tsx:33
msgid "Toggle theme"
msgstr "Переключить тему"
#: src/lib/utils.ts:317
msgid "Triggers when any sensor exceeds a threshold"
msgstr "Срабатывает, когда любой датчик превышает порог"
#: src/lib/utils.ts:310
msgid "Triggers when combined up/down exceeds a threshold"
msgstr "Срабатывает, когда комбинированный вход/выход превышает порог"
#: src/lib/utils.ts:292
msgid "Triggers when CPU usage exceeds a threshold"
msgstr "Срабатывает, когда использование CPU превышает порог"
#: src/lib/utils.ts:298
msgid "Triggers when memory usage exceeds a threshold"
msgstr "Срабатывает, когда использование памяти превышает порог"
#: src/lib/utils.ts:285
msgid "Triggers when status switches between up and down"
msgstr "Срабатывает, когда статус переключается между включено и выключено"
#: src/lib/utils.ts:304
msgid "Triggers when usage of any disk exceeds a threshold"
msgstr "Срабатывает, когда использование любого диска превышает порог"
#: src/components/systems-table/systems-table.tsx:320
msgid "Updated in real time. Click on a system to view information."
msgstr "Обновляется в реальном времени. Нажмите на систему, чтобы просмотреть информацию."
#: src/components/routes/system.tsx:253
msgid "Uptime"
msgstr "Время работы"
#: src/components/routes/system.tsx:494
msgid "Usage"
msgstr "Использование"
#: src/components/routes/system.tsx:415
msgid "Usage of root partition"
msgstr "Использование корневого раздела"
#: src/components/charts/mem-chart.tsx:65
#: src/components/charts/swap-chart.tsx:56
msgid "Used"
msgstr "Использовано"
#: src/components/login/auth-form.tsx:138
msgid "username"
msgstr "имя пользователя"
#: src/components/login/auth-form.tsx:131
msgid "Username"
msgstr "Имя пользователя"
#: src/components/command-palette.tsx:143
#: src/components/navbar.tsx:70
msgid "Users"
msgstr "Пользователи"
#: src/components/routes/system.tsx:603
msgid "Waiting for enough records to display"
msgstr "Ожидание достаточного количества записей для отображения"
#: src/components/routes/settings/general.tsx:48
msgid "Want to help us make our translations even better? Check out <0>Crowdin</0> for more details."
msgstr "Хотите помочь нам улучшить наши переводы? Посетите <0>Crowdin</0> для получения более подробной информации."
#: src/components/routes/settings/notifications.tsx:124
msgid "Webhook / Push notifications"
msgstr "Webhook / Push уведомления"
#. Context is disk write
#: src/components/charts/area-chart.tsx:55
#: src/components/charts/area-chart.tsx:65
msgid "Write"
msgstr "Запись"
#: src/components/routes/settings/layout.tsx:61
msgid "YAML Config"
msgstr "YAML конфигурация"
#: src/components/routes/settings/config-yaml.tsx:45
msgid "YAML Configuration"
msgstr "YAML конфигурация"
#: src/components/routes/settings/layout.tsx:34
msgid "Your user settings have been updated."
msgstr "Ваши настройки пользователя были обновлены."

View File

@@ -0,0 +1,808 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2024-11-01 11:30-0400\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: tr\n"
"Project-Id-Version: beszel\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-11-04 20:46\n"
"Last-Translator: \n"
"Language-Team: Turkish\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: beszel\n"
"X-Crowdin-Project-ID: 733311\n"
"X-Crowdin-Language: tr\n"
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 16\n"
#: src/components/routes/system.tsx:242
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# gün} other {# gün}}"
#: src/components/routes/system.tsx:240
msgid "{hours, plural, one {# hour} other {# hours}}"
msgstr "{hours, plural, one {# saat} other {# saat}}"
#: src/lib/utils.ts:139
msgid "1 hour"
msgstr "1 saat"
#: src/lib/utils.ts:162
msgid "1 week"
msgstr "1 hafta"
#: src/lib/utils.ts:147
msgid "12 hours"
msgstr "12 saat"
#: src/lib/utils.ts:155
msgid "24 hours"
msgstr "24 saat"
#: src/lib/utils.ts:170
msgid "30 days"
msgstr "30 gün"
#. Table column
#: src/components/systems-table/systems-table.tsx:207
msgid "Actions"
msgstr "Eylemler"
#: src/components/routes/home.tsx:62
msgid "Active Alerts"
msgstr "Aktif Uyarılar"
#: src/components/add-system.tsx:74
msgid "Add <0>System</0>"
msgstr "<0>Sistem</0> Ekle"
#: src/components/add-system.tsx:83
msgid "Add New System"
msgstr "Yeni Sistem Ekle"
#: src/components/add-system.tsx:167
#: src/components/add-system.tsx:178
msgid "Add system"
msgstr "Sistem ekle"
#: src/components/routes/settings/notifications.tsx:156
msgid "Add URL"
msgstr "URL Ekle"
#: src/components/routes/settings/general.tsx:81
msgid "Adjust display options for charts."
msgstr "Grafikler için görüntüleme seçeneklerini ayarlayın."
#: src/components/command-palette.tsx:133
#: src/components/command-palette.tsx:146
#: src/components/command-palette.tsx:160
#: src/components/command-palette.tsx:174
#: src/components/command-palette.tsx:189
#: src/components/command-palette.tsx:204
msgid "Admin"
msgstr "Yönetici"
#: src/components/systems-table/systems-table.tsx:186
msgid "Agent"
msgstr "Aracı"
#: src/components/alerts/alert-button.tsx:32
#: src/components/alerts/alert-button.tsx:68
msgid "Alerts"
msgstr "Uyarılar"
#: src/components/alerts/alert-button.tsx:88
#: src/components/systems-table/systems-table.tsx:317
msgid "All Systems"
msgstr "Tüm Sistemler"
#: src/components/systems-table/systems-table.tsx:261
msgid "Are you sure you want to delete {name}?"
msgstr "{name} silmek istediğinizden emin misiniz?"
#: src/components/command-palette.tsx:186
#: src/components/navbar.tsx:102
msgid "Auth Providers"
msgstr "Kimlik Sağlayıcılar"
#: src/components/copy-to-clipboard.tsx:16
msgid "Automatic copy requires a secure context."
msgstr "Otomatik kopyalama güvenli bir bağlam gerektirir."
#: src/components/routes/system.tsx:568
msgid "Average"
msgstr "Ortalama"
#: src/components/routes/system.tsx:387
msgid "Average CPU utilization of containers"
msgstr "Konteynerlerin ortalama CPU kullanımı"
#: src/components/alerts/alerts-system.tsx:204
msgid "Average exceeds <0>{value}{0}</0>"
msgstr "Ortalama <0>{value}{0}</0> aşıyor"
#: src/components/routes/system.tsx:376
msgid "Average system-wide CPU utilization"
msgstr "Sistem genelinde ortalama CPU kullanımı"
#: src/components/command-palette.tsx:171
#: src/components/navbar.tsx:94
msgid "Backups"
msgstr "Yedekler"
#: src/components/routes/system.tsx:436
#: src/lib/utils.ts:307
msgid "Bandwidth"
msgstr "Bant Genişliği"
#: src/components/login/auth-form.tsx:313
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
msgstr "Beszel, OpenID Connect ve birçok OAuth2 kimlik doğrulama sağlayıcısını destekler."
#: src/components/routes/settings/notifications.tsx:127
msgid "Beszel uses <0>Shoutrrr</0> to integrate with popular notification services."
msgstr "Beszel, popüler bildirim hizmetleriyle entegre olmak için <0>Shoutrrr</0> kullanır."
#: src/components/add-system.tsx:88
msgid "Binary"
msgstr "İkili"
#: src/components/charts/mem-chart.tsx:89
msgid "Cache / Buffers"
msgstr "Önbellek / Tamponlar"
#: src/components/systems-table/systems-table.tsx:272
msgid "Cancel"
msgstr "İptal"
#: src/components/routes/settings/config-yaml.tsx:68
msgid "Caution - potential data loss"
msgstr "Dikkat - potansiyel veri kaybı"
#: src/components/routes/settings/general.tsx:36
msgid "Change general application options."
msgstr "Genel uygulama seçeneklerini değiştirin."
#: src/components/routes/settings/general.tsx:78
msgid "Chart options"
msgstr "Grafik seçenekleri"
#: src/components/login/forgot-pass-form.tsx:34
msgid "Check {email} for a reset link."
msgstr "Sıfırlama bağlantısı için {email} kontrol edin."
#: src/components/routes/settings/layout.tsx:40
msgid "Check logs for more details."
msgstr "Daha fazla ayrıntı için günlükleri kontrol edin."
#: src/components/routes/settings/notifications.tsx:183
msgid "Check your notification service"
msgstr "Bildirim hizmetinizi kontrol edin"
#: src/components/add-system.tsx:153
msgid "Click to copy"
msgstr "Kopyalamak için tıklayın"
#. Context: table columns
#: src/components/systems-table/systems-table.tsx:328
msgid "Columns"
msgstr "Sütunlar"
#: src/components/login/forgot-pass-form.tsx:83
#: src/components/login/forgot-pass-form.tsx:89
msgid "Command line instructions"
msgstr "Komut satırı talimatları"
#: src/components/routes/settings/notifications.tsx:77
msgid "Configure how you receive alert notifications."
msgstr "Uyarı bildirimlerini nasıl alacağınızı yapılandırın."
#: src/components/login/auth-form.tsx:189
#: src/components/login/auth-form.tsx:194
msgid "Confirm password"
msgstr "Şifreyi onayla"
#: src/components/systems-table/systems-table.tsx:278
msgid "Continue"
msgstr "Devam et"
#: src/lib/utils.ts:25
msgid "Copied to clipboard"
msgstr "Panoya kopyalandı"
#: src/components/add-system.tsx:164
msgid "Copy"
msgstr "Kopyala"
#: src/components/systems-table/systems-table.tsx:247
msgid "Copy host"
msgstr "Ana bilgisayarı kopyala"
#: src/components/add-system.tsx:175
msgid "Copy Linux command"
msgstr "Linux komutunu kopyala"
#: src/components/copy-to-clipboard.tsx:13
msgid "Copy text"
msgstr "Metni kopyala"
#: src/components/systems-table/systems-table.tsx:152
msgid "CPU"
msgstr "CPU"
#: src/components/charts/area-chart.tsx:52
#: src/components/routes/system.tsx:375
#: src/lib/utils.ts:289
msgid "CPU Usage"
msgstr "CPU Kullanımı"
#: src/components/login/auth-form.tsx:215
msgid "Create account"
msgstr "Hesap oluştur"
#. Dark theme
#: src/components/mode-toggle.tsx:21
msgid "Dark"
msgstr "Koyu"
#: src/components/command-palette.tsx:82
#: src/components/routes/home.tsx:35
msgid "Dashboard"
msgstr "Gösterge Paneli"
#: src/components/routes/settings/general.tsx:85
msgid "Default time period"
msgstr "Varsayılan zaman dilimi"
#: src/components/systems-table/systems-table.tsx:253
msgid "Delete"
msgstr "Sil"
#: src/components/systems-table/systems-table.tsx:166
msgid "Disk"
msgstr "Disk"
#: src/components/routes/system.tsx:426
msgid "Disk I/O"
msgstr "Disk G/Ç"
#: src/components/charts/disk-chart.tsx:74
#: src/components/routes/system.tsx:415
#: src/lib/utils.ts:301
msgid "Disk Usage"
msgstr "Disk Kullanımı"
#: src/components/routes/system.tsx:495
msgid "Disk usage of {extraFsName}"
msgstr "{extraFsName} disk kullanımı"
#: src/components/routes/system.tsx:386
msgid "Docker CPU Usage"
msgstr "Docker CPU Kullanımı"
#: src/components/routes/system.tsx:407
msgid "Docker Memory Usage"
msgstr "Docker Bellek Kullanımı"
#: src/components/routes/system.tsx:452
msgid "Docker Network I/O"
msgstr "Docker Ağ G/Ç"
#: src/components/command-palette.tsx:125
msgid "Documentation"
msgstr "Dokümantasyon"
#: src/components/login/auth-form.tsx:158
msgid "email"
msgstr "e-posta"
#: src/components/login/auth-form.tsx:152
#: src/components/login/forgot-pass-form.tsx:53
msgid "Email"
msgstr "E-posta"
#: src/components/routes/settings/notifications.tsx:91
msgid "Email notifications"
msgstr "E-posta bildirimleri"
#: src/components/login/login.tsx:36
msgid "Enter email address to reset password"
msgstr "Şifreyi sıfırlamak için e-posta adresini girin"
#: src/components/routes/settings/notifications.tsx:111
msgid "Enter email address..."
msgstr "E-posta adresini girin..."
#: src/components/login/auth-form.tsx:256
#: src/components/routes/settings/config-yaml.tsx:28
#: src/components/routes/settings/notifications.tsx:187
msgid "Error"
msgstr "Hata"
#: src/components/routes/home.tsx:81
msgid "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
msgstr "Son {2, plural, one {# dakika} other {# dakika}} içinde {0}{1} aşıyor"
#: src/components/routes/settings/config-yaml.tsx:72
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
msgstr "<0>config.yml</0> içinde tanımlanmayan mevcut sistemler silinecektir. Lütfen düzenli yedekler alın."
#: src/components/routes/settings/config-yaml.tsx:93
msgid "Export configuration"
msgstr "Yapılandırmayı dışa aktar"
#: src/components/routes/settings/config-yaml.tsx:48
msgid "Export your current systems configuration."
msgstr "Mevcut sistem yapılandırmanızı dışa aktarın."
#: src/lib/utils.ts:38
msgid "Failed to authenticate"
msgstr "Kimlik doğrulama başarısız"
#: src/components/routes/settings/layout.tsx:39
#: src/components/routes/settings/notifications.tsx:62
msgid "Failed to save settings"
msgstr "Ayarlar kaydedilemedi"
#: src/components/routes/settings/notifications.tsx:188
msgid "Failed to send test notification"
msgstr "Test bildirimi gönderilemedi"
#: src/components/alerts/alerts-system.tsx:27
msgid "Failed to update alert"
msgstr "Uyarı güncellenemedi"
#: src/components/routes/system.tsx:539
#: src/components/systems-table/systems-table.tsx:324
msgid "Filter..."
msgstr "Filtrele..."
#: src/components/alerts/alerts-system.tsx:225
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
msgstr "<0>{min}</0> {min, plural, one {dakika} other {dakika}} için"
#: src/components/login/auth-form.tsx:337
msgid "Forgot password?"
msgstr "Şifrenizi mi unuttunuz?"
#. Context: General settings
#: src/components/routes/settings/general.tsx:33
#: src/components/routes/settings/layout.tsx:51
msgid "General"
msgstr "Genel"
#: src/components/add-system.tsx:119
msgid "Host / IP"
msgstr "Host / IP"
#: src/components/login/forgot-pass-form.tsx:93
msgid "If you've lost the password to your admin account, you may reset it using the following command."
msgstr "Yönetici hesabınızın şifresini kaybettiyseniz, aşağıdaki komutu kullanarak sıfırlayabilirsiniz."
#: src/components/login/auth-form.tsx:16
msgid "Invalid email address."
msgstr "Geçersiz e-posta adresi."
#. Linux kernel
#: src/components/routes/system.tsx:254
msgid "Kernel"
msgstr "Çekirdek"
#: src/components/routes/settings/general.tsx:45
msgid "Language"
msgstr "Dil"
#. Light theme
#: src/components/mode-toggle.tsx:16
msgid "Light"
msgstr "Açık"
#: src/components/navbar.tsx:113
msgid "Log Out"
msgstr "Çıkış Yap"
#: src/components/login/login.tsx:17
msgid "Login"
msgstr "Giriş Yap"
#: src/components/login/auth-form.tsx:42
#: src/components/login/forgot-pass-form.tsx:15
msgid "Login attempt failed"
msgstr "Giriş denemesi başarısız"
#: src/components/command-palette.tsx:157
#: src/components/navbar.tsx:86
msgid "Logs"
msgstr "Günlükler"
#: src/components/routes/settings/notifications.tsx:80
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
msgstr "Uyarı oluşturma yerini mi arıyorsunuz? Sistemler tablosundaki zil <0/> simgelerine tıklayın."
#: src/components/routes/settings/layout.tsx:85
msgid "Manage display and notification preferences."
msgstr "Görüntüleme ve bildirim tercihlerini yönetin."
#. Chart select field. Please try to keep this short.
#: src/components/routes/system.tsx:571
msgid "Max 1 min"
msgstr "Maks 1 dk"
#: src/components/systems-table/systems-table.tsx:159
msgid "Memory"
msgstr "Bellek"
#: src/components/routes/system.tsx:397
#: src/lib/utils.ts:295
msgid "Memory Usage"
msgstr "Bellek Kullanımı"
#: src/components/routes/system.tsx:408
msgid "Memory usage of docker containers"
msgstr "Docker konteynerlerinin bellek kullanımı"
#: src/components/add-system.tsx:113
msgid "Name"
msgstr "Ad"
#: src/components/systems-table/systems-table.tsx:173
msgid "Net"
msgstr "Ağ"
#: src/components/routes/system.tsx:453
msgid "Network traffic of docker containers"
msgstr "Docker konteynerlerinin ağ trafiği"
#: src/components/routes/system.tsx:438
msgid "Network traffic of public interfaces"
msgstr "Genel arayüzlerin ağ trafiği"
#: src/components/command-palette.tsx:50
msgid "No results found."
msgstr "Sonuç bulunamadı."
#: src/components/systems-table/systems-table.tsx:400
msgid "No systems found."
msgstr "Sistem bulunamadı."
#: src/components/command-palette.tsx:111
#: src/components/routes/settings/layout.tsx:56
#: src/components/routes/settings/notifications.tsx:74
msgid "Notifications"
msgstr "Bildirimler"
#: src/components/login/auth-form.tsx:308
msgid "OAuth 2 / OIDC support"
msgstr "OAuth 2 / OIDC desteği"
#: src/components/routes/settings/config-yaml.tsx:61
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
msgstr "Her yeniden başlatmada, veritabanındaki sistemler dosyada tanımlanan sistemlerle eşleşecek şekilde güncellenecektir."
#: src/components/systems-table/systems-table.tsx:219
msgid "Open menu"
msgstr "Menüyü aç"
#: src/components/login/auth-form.tsx:227
msgid "Or continue with"
msgstr "Veya devam et"
#: src/components/alerts/alert-button.tsx:109
msgid "Overwrite existing alerts"
msgstr "Mevcut uyarıların üzerine yaz"
#: src/components/command-palette.tsx:85
msgid "Page"
msgstr "Sayfa"
#: src/components/command-palette.tsx:72
msgid "Pages / Settings"
msgstr "Sayfalar / Ayarlar"
#: src/components/login/auth-form.tsx:171
#: src/components/login/auth-form.tsx:176
msgid "Password"
msgstr "Şifre"
#: src/components/login/auth-form.tsx:17
msgid "Password must be at least 10 characters."
msgstr "Şifre en az 10 karakter olmalıdır."
#: src/components/login/forgot-pass-form.tsx:33
msgid "Password reset request received"
msgstr "Şifre sıfırlama isteği alındı"
#: src/components/systems-table/systems-table.tsx:241
msgid "Pause"
msgstr "Duraklat"
#: src/components/routes/settings/notifications.tsx:95
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
msgstr "Uyarıların teslim edilmesini sağlamak için lütfen bir SMTP sunucusu <0>yapılandırın</0>."
#: src/components/alerts/alerts-system.tsx:28
msgid "Please check logs for more details."
msgstr "Daha fazla ayrıntı için lütfen günlükleri kontrol edin."
#: src/components/login/auth-form.tsx:43
#: src/components/login/forgot-pass-form.tsx:16
msgid "Please check your credentials and try again"
msgstr "Lütfen kimlik bilgilerinizi kontrol edin ve tekrar deneyin"
#: src/components/login/login.tsx:34
msgid "Please create an admin account"
msgstr "Lütfen bir yönetici hesabı oluşturun"
#: src/components/login/auth-form.tsx:257
msgid "Please enable pop-ups for this site"
msgstr "Lütfen bu site için açılır pencereleri etkinleştirin"
#: src/lib/utils.ts:39
msgid "Please log in again"
msgstr "Lütfen tekrar giriş yapın"
#: src/components/login/auth-form.tsx:316
msgid "Please see <0>the documentation</0> for instructions."
msgstr "Talimatlar için lütfen <0>dokümantasyonu</0> inceleyin."
#: src/components/login/login.tsx:38
msgid "Please sign in to your account"
msgstr "Lütfen hesabınıza giriş yapın"
#: src/components/add-system.tsx:125
msgid "Port"
msgstr "Port"
#: src/components/routes/system.tsx:398
msgid "Precise utilization at the recorded time"
msgstr "Kayıtlı zamanda kesin kullanım"
#: src/components/routes/settings/general.tsx:58
msgid "Preferred Language"
msgstr "Tercih Edilen Dil"
#. Use 'Key' if your language requires many more characters
#: src/components/add-system.tsx:131
msgid "Public Key"
msgstr "Genel Anahtar"
#. Context is disk read
#: src/components/charts/area-chart.tsx:56
#: src/components/charts/area-chart.tsx:66
msgid "Read"
msgstr "Oku"
#. Context is network bytes received (download)
#: src/components/charts/area-chart.tsx:61
msgid "Received"
msgstr "Alındı"
#: src/components/login/forgot-pass-form.tsx:76
msgid "Reset Password"
msgstr "Şifreyi Sıfırla"
#: src/components/systems-table/systems-table.tsx:236
msgid "Resume"
msgstr "Devam et"
#: src/components/routes/settings/notifications.tsx:117
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
msgstr "Adresleri enter tuşu veya virgül ile kaydedin. E-posta bildirimlerini devre dışı bırakmak için boş bırakın."
#: src/components/routes/settings/general.tsx:106
#: src/components/routes/settings/notifications.tsx:167
msgid "Save Settings"
msgstr "Ayarları Kaydet"
#: src/components/navbar.tsx:142
msgid "Search"
msgstr "Ara"
#: src/components/command-palette.tsx:47
msgid "Search for systems or settings..."
msgstr "Sistemler veya ayarlar için ara..."
#: src/components/alerts/alert-button.tsx:71
msgid "See <0>notification settings</0> to configure how you receive alerts."
msgstr "Uyarıları nasıl alacağınızı yapılandırmak için <0>bildirim ayarlarını</0> inceleyin."
#. Context is network bytes sent (upload)
#: src/components/charts/area-chart.tsx:60
msgid "Sent"
msgstr "Gönderildi"
#: src/components/routes/settings/general.tsx:100
msgid "Sets the default time range for charts when a system is viewed."
msgstr "Bir sistem görüntülendiğinde grafikler için varsayılan zaman aralığını ayarlar."
#: src/components/command-palette.tsx:96
#: src/components/command-palette.tsx:99
#: src/components/command-palette.tsx:114
#: src/components/routes/settings/layout.tsx:71
#: src/components/routes/settings/layout.tsx:82
msgid "Settings"
msgstr "Ayarlar"
#: src/components/routes/settings/layout.tsx:33
msgid "Settings saved"
msgstr "Ayarlar kaydedildi"
#: src/components/login/auth-form.tsx:215
msgid "Sign in"
msgstr "Giriş yap"
#: src/components/command-palette.tsx:201
msgid "SMTP settings"
msgstr "SMTP ayarları"
#: src/lib/utils.ts:282
msgid "Status"
msgstr "Durum"
#: src/components/routes/system.tsx:467
msgid "Swap space used by the system"
msgstr "Sistem tarafından kullanılan takas alanı"
#: src/components/routes/system.tsx:466
msgid "Swap Usage"
msgstr "Takas Kullanımı"
#. System theme
#: src/components/mode-toggle.tsx:26
#: src/components/systems-table/systems-table.tsx:110
#: src/components/systems-table/systems-table.tsx:121
msgid "System"
msgstr "Sistem"
#: src/components/navbar.tsx:78
msgid "Systems"
msgstr "Sistemler"
#: src/components/routes/settings/config-yaml.tsx:55
msgid "Systems may be managed in a <0>config.yml</0> file inside your data directory."
msgstr "Sistemler, veri dizininizdeki bir <0>config.yml</0> dosyasında yönetilebilir."
#: src/components/routes/system.tsx:477
#: src/lib/utils.ts:314
msgid "Temperature"
msgstr "Sıcaklık"
#: src/components/routes/system.tsx:478
msgid "Temperatures of system sensors"
msgstr "Sistem sensörlerinin sıcaklıkları"
#: src/components/routes/settings/notifications.tsx:211
msgid "Test <0>URL</0>"
msgstr "Test <0>URL</0>"
#: src/components/routes/settings/notifications.tsx:182
msgid "Test notification sent"
msgstr "Test bildirimi gönderildi"
#: src/components/add-system.tsx:104
msgid "The agent must be running on the system to connect. Copy the installation command for the agent below."
msgstr "Bağlanmak için aracının sistemde çalışıyor olması gerekir. Aşağıdaki aracı kurulum komutunu kopyalayın."
#: src/components/add-system.tsx:95
msgid "The agent must be running on the system to connect. Copy the<0>docker-compose.yml</0> for the agent below."
msgstr "Bağlanmak için aracının sistemde çalışıyor olması gerekir. Aşağıdaki <0>docker-compose.yml</0> dosyasını kopyalayın."
#: src/components/login/forgot-pass-form.tsx:98
msgid "Then log into the backend and reset your user account password in the users table."
msgstr "Ardından arka uca giriş yapın ve kullanıcılar tablosunda kullanıcı hesabı şifrenizi sıfırlayın."
#: src/components/systems-table/systems-table.tsx:264
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
msgstr "Bu işlem geri alınamaz. Bu, veritabanından {name} için tüm mevcut kayıtları kalıcı olarak silecektir."
#: src/components/routes/system.tsx:507
msgid "Throughput of {extraFsName}"
msgstr "{extraFsName} verimliliği"
#: src/components/routes/system.tsx:427
msgid "Throughput of root filesystem"
msgstr "Kök dosya sisteminin verimliliği"
#: src/components/routes/settings/notifications.tsx:106
msgid "To email(s)"
msgstr "E-posta(lar)a"
#: src/components/routes/system.tsx:350
#: src/components/routes/system.tsx:363
msgid "Toggle grid"
msgstr "Izgarayı değiştir"
#: src/components/mode-toggle.tsx:33
msgid "Toggle theme"
msgstr "Temayı değiştir"
#: src/lib/utils.ts:317
msgid "Triggers when any sensor exceeds a threshold"
msgstr "Herhangi bir sensör bir eşiği aştığında tetiklenir"
#: src/lib/utils.ts:310
msgid "Triggers when combined up/down exceeds a threshold"
msgstr "Birleştirilmiş yukarı/aşağı bir eşiği aştığında tetiklenir"
#: src/lib/utils.ts:292
msgid "Triggers when CPU usage exceeds a threshold"
msgstr "CPU kullanımı bir eşiği aştığında tetiklenir"
#: src/lib/utils.ts:298
msgid "Triggers when memory usage exceeds a threshold"
msgstr "Bellek kullanımı bir eşiği aştığında tetiklenir"
#: src/lib/utils.ts:285
msgid "Triggers when status switches between up and down"
msgstr "Durum yukarı ve aşağı arasında değiştiğinde tetiklenir"
#: src/lib/utils.ts:304
msgid "Triggers when usage of any disk exceeds a threshold"
msgstr "Herhangi bir diskin kullanımı bir eşiği aştığında tetiklenir"
#: src/components/systems-table/systems-table.tsx:320
msgid "Updated in real time. Click on a system to view information."
msgstr "Gerçek zamanlı olarak güncellenir. Bilgileri görüntülemek için bir sisteme tıklayın."
#: src/components/routes/system.tsx:253
msgid "Uptime"
msgstr "Çalışma Süresi"
#: src/components/routes/system.tsx:494
msgid "Usage"
msgstr "Kullanım"
#: src/components/routes/system.tsx:415
msgid "Usage of root partition"
msgstr "Kök bölümün kullanımı"
#: src/components/charts/mem-chart.tsx:65
#: src/components/charts/swap-chart.tsx:56
msgid "Used"
msgstr "Kullanıldı"
#: src/components/login/auth-form.tsx:138
msgid "username"
msgstr "kullanıcı adı"
#: src/components/login/auth-form.tsx:131
msgid "Username"
msgstr "Kullanıcı Adı"
#: src/components/command-palette.tsx:143
#: src/components/navbar.tsx:70
msgid "Users"
msgstr "Kullanıcılar"
#: src/components/routes/system.tsx:603
msgid "Waiting for enough records to display"
msgstr "Görüntülemek için yeterli kayıt bekleniyor"
#: src/components/routes/settings/general.tsx:48
msgid "Want to help us make our translations even better? Check out <0>Crowdin</0> for more details."
msgstr "Çevirilerimizi daha iyi hale getirmemize yardımcı olmak ister misiniz? Daha fazla bilgi için <0>Crowdin</0> inceleyin."
#: src/components/routes/settings/notifications.tsx:124
msgid "Webhook / Push notifications"
msgstr "Webhook / Anlık bildirimler"
#. Context is disk write
#: src/components/charts/area-chart.tsx:55
#: src/components/charts/area-chart.tsx:65
msgid "Write"
msgstr "Yaz"
#: src/components/routes/settings/layout.tsx:61
msgid "YAML Config"
msgstr "YAML Yapılandırması"
#: src/components/routes/settings/config-yaml.tsx:45
msgid "YAML Configuration"
msgstr "YAML Yapılandırması"
#: src/components/routes/settings/layout.tsx:34
msgid "Your user settings have been updated."
msgstr "Kullanıcı ayarlarınız güncellendi."

View File

@@ -0,0 +1,808 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2024-11-01 11:30-0400\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: uk\n"
"Project-Id-Version: beszel\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-11-04 20:46\n"
"Last-Translator: \n"
"Language-Team: Ukrainian\n"
"Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n"
"X-Crowdin-Project: beszel\n"
"X-Crowdin-Project-ID: 733311\n"
"X-Crowdin-Language: uk\n"
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 16\n"
#: src/components/routes/system.tsx:242
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# день} few {# дні} many {# днів} other {# дня}}"
#: src/components/routes/system.tsx:240
msgid "{hours, plural, one {# hour} other {# hours}}"
msgstr "{hours, plural, one {# година} few {# години} many {# годин} other {# години}}"
#: src/lib/utils.ts:139
msgid "1 hour"
msgstr "1 година"
#: src/lib/utils.ts:162
msgid "1 week"
msgstr "1 тиждень"
#: src/lib/utils.ts:147
msgid "12 hours"
msgstr "12 годин"
#: src/lib/utils.ts:155
msgid "24 hours"
msgstr "24 години"
#: src/lib/utils.ts:170
msgid "30 days"
msgstr "30 днів"
#. Table column
#: src/components/systems-table/systems-table.tsx:207
msgid "Actions"
msgstr "Дії"
#: src/components/routes/home.tsx:62
msgid "Active Alerts"
msgstr "Активні сповіщення"
#: src/components/add-system.tsx:74
msgid "Add <0>System</0>"
msgstr "Додати <0>Систему</0>"
#: src/components/add-system.tsx:83
msgid "Add New System"
msgstr "Додати нову систему"
#: src/components/add-system.tsx:167
#: src/components/add-system.tsx:178
msgid "Add system"
msgstr "Додати систему"
#: src/components/routes/settings/notifications.tsx:156
msgid "Add URL"
msgstr "Додати URL"
#: src/components/routes/settings/general.tsx:81
msgid "Adjust display options for charts."
msgstr "Налаштуйте параметри відображення для графіків."
#: src/components/command-palette.tsx:133
#: src/components/command-palette.tsx:146
#: src/components/command-palette.tsx:160
#: src/components/command-palette.tsx:174
#: src/components/command-palette.tsx:189
#: src/components/command-palette.tsx:204
msgid "Admin"
msgstr "Адміністратор"
#: src/components/systems-table/systems-table.tsx:186
msgid "Agent"
msgstr "Агент"
#: src/components/alerts/alert-button.tsx:32
#: src/components/alerts/alert-button.tsx:68
msgid "Alerts"
msgstr "Сповіщення"
#: src/components/alerts/alert-button.tsx:88
#: src/components/systems-table/systems-table.tsx:317
msgid "All Systems"
msgstr "Всі системи"
#: src/components/systems-table/systems-table.tsx:261
msgid "Are you sure you want to delete {name}?"
msgstr "Ви впевнені, що хочете видалити {name}?"
#: src/components/command-palette.tsx:186
#: src/components/navbar.tsx:102
msgid "Auth Providers"
msgstr "Постачальники автентифікації"
#: src/components/copy-to-clipboard.tsx:16
msgid "Automatic copy requires a secure context."
msgstr "Автоматичне копіювання вимагає безпечного контексту."
#: src/components/routes/system.tsx:568
msgid "Average"
msgstr "Середнє"
#: src/components/routes/system.tsx:387
msgid "Average CPU utilization of containers"
msgstr "Середнє використання CPU контейнерами"
#: src/components/alerts/alerts-system.tsx:204
msgid "Average exceeds <0>{value}{0}</0>"
msgstr "Середнє перевищує <0>{value}{0}</0>"
#: src/components/routes/system.tsx:376
msgid "Average system-wide CPU utilization"
msgstr "Середнє використання CPU по всій системі"
#: src/components/command-palette.tsx:171
#: src/components/navbar.tsx:94
msgid "Backups"
msgstr "Резервні копії"
#: src/components/routes/system.tsx:436
#: src/lib/utils.ts:307
msgid "Bandwidth"
msgstr "Пропускна здатність"
#: src/components/login/auth-form.tsx:313
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
msgstr "Beszel підтримує OpenID Connect та багато постачальників автентифікації OAuth2."
#: src/components/routes/settings/notifications.tsx:127
msgid "Beszel uses <0>Shoutrrr</0> to integrate with popular notification services."
msgstr "Beszel використовує <0>Shoutrrr</0> для інтеграції з популярними сервісами сповіщень."
#: src/components/add-system.tsx:88
msgid "Binary"
msgstr "Двійковий"
#: src/components/charts/mem-chart.tsx:89
msgid "Cache / Buffers"
msgstr "Кеш / Буфери"
#: src/components/systems-table/systems-table.tsx:272
msgid "Cancel"
msgstr "Скасувати"
#: src/components/routes/settings/config-yaml.tsx:68
msgid "Caution - potential data loss"
msgstr "Увага - можливе втрата даних"
#: src/components/routes/settings/general.tsx:36
msgid "Change general application options."
msgstr "Змінити загальні параметри програми."
#: src/components/routes/settings/general.tsx:78
msgid "Chart options"
msgstr "Параметри графіків"
#: src/components/login/forgot-pass-form.tsx:34
msgid "Check {email} for a reset link."
msgstr "Перевірте {email} для отримання посилання на скидання."
#: src/components/routes/settings/layout.tsx:40
msgid "Check logs for more details."
msgstr "Перевірте журнали для отримання додаткової інформації."
#: src/components/routes/settings/notifications.tsx:183
msgid "Check your notification service"
msgstr "Перевірте свій сервіс сповіщень"
#: src/components/add-system.tsx:153
msgid "Click to copy"
msgstr "Натисніть, щоб скопіювати"
#. Context: table columns
#: src/components/systems-table/systems-table.tsx:328
msgid "Columns"
msgstr "Стовпці"
#: src/components/login/forgot-pass-form.tsx:83
#: src/components/login/forgot-pass-form.tsx:89
msgid "Command line instructions"
msgstr "Інструкції командного рядка"
#: src/components/routes/settings/notifications.tsx:77
msgid "Configure how you receive alert notifications."
msgstr "Налаштуйте, як ви отримуєте сповіщення про тривоги."
#: src/components/login/auth-form.tsx:189
#: src/components/login/auth-form.tsx:194
msgid "Confirm password"
msgstr "Підтвердьте пароль"
#: src/components/systems-table/systems-table.tsx:278
msgid "Continue"
msgstr "Продовжити"
#: src/lib/utils.ts:25
msgid "Copied to clipboard"
msgstr "Скопійовано в буфер обміну"
#: src/components/add-system.tsx:164
msgid "Copy"
msgstr "Копіювати"
#: src/components/systems-table/systems-table.tsx:247
msgid "Copy host"
msgstr "Копіювати хост"
#: src/components/add-system.tsx:175
msgid "Copy Linux command"
msgstr "Копіювати команду Linux"
#: src/components/copy-to-clipboard.tsx:13
msgid "Copy text"
msgstr "Копіювати текст"
#: src/components/systems-table/systems-table.tsx:152
msgid "CPU"
msgstr "ЦП"
#: src/components/charts/area-chart.tsx:52
#: src/components/routes/system.tsx:375
#: src/lib/utils.ts:289
msgid "CPU Usage"
msgstr "Використання ЦП"
#: src/components/login/auth-form.tsx:215
msgid "Create account"
msgstr "Створити обліковий запис"
#. Dark theme
#: src/components/mode-toggle.tsx:21
msgid "Dark"
msgstr "Темний"
#: src/components/command-palette.tsx:82
#: src/components/routes/home.tsx:35
msgid "Dashboard"
msgstr "Панель управління"
#: src/components/routes/settings/general.tsx:85
msgid "Default time period"
msgstr "Стандартний період часу"
#: src/components/systems-table/systems-table.tsx:253
msgid "Delete"
msgstr "Видалити"
#: src/components/systems-table/systems-table.tsx:166
msgid "Disk"
msgstr "Диск"
#: src/components/routes/system.tsx:426
msgid "Disk I/O"
msgstr "Дисковий ввід/вивід"
#: src/components/charts/disk-chart.tsx:74
#: src/components/routes/system.tsx:415
#: src/lib/utils.ts:301
msgid "Disk Usage"
msgstr "Використання диска"
#: src/components/routes/system.tsx:495
msgid "Disk usage of {extraFsName}"
msgstr "Використання диска {extraFsName}"
#: src/components/routes/system.tsx:386
msgid "Docker CPU Usage"
msgstr "Використання ЦП Docker"
#: src/components/routes/system.tsx:407
msgid "Docker Memory Usage"
msgstr "Використання пам'яті Docker"
#: src/components/routes/system.tsx:452
msgid "Docker Network I/O"
msgstr "Мережевий ввід/вивід Docker"
#: src/components/command-palette.tsx:125
msgid "Documentation"
msgstr "Документація"
#: src/components/login/auth-form.tsx:158
msgid "email"
msgstr "електронна пошта"
#: src/components/login/auth-form.tsx:152
#: src/components/login/forgot-pass-form.tsx:53
msgid "Email"
msgstr "Електронна пошта"
#: src/components/routes/settings/notifications.tsx:91
msgid "Email notifications"
msgstr "Сповіщення електронною поштою"
#: src/components/login/login.tsx:36
msgid "Enter email address to reset password"
msgstr "Введіть адресу електронної пошти для скидання пароля"
#: src/components/routes/settings/notifications.tsx:111
msgid "Enter email address..."
msgstr "Введіть адресу електронної пошти..."
#: src/components/login/auth-form.tsx:256
#: src/components/routes/settings/config-yaml.tsx:28
#: src/components/routes/settings/notifications.tsx:187
msgid "Error"
msgstr "Помилка"
#: src/components/routes/home.tsx:81
msgid "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
msgstr "Перевищує {0}{1} за останні {2, plural, one {# хвилину} other {# хвилин}}"
#: src/components/routes/settings/config-yaml.tsx:72
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
msgstr "Існуючі системи, не визначені в <0>config.yml</0>, будуть видалені. Будь ласка, робіть регулярні резервні копії."
#: src/components/routes/settings/config-yaml.tsx:93
msgid "Export configuration"
msgstr "Експорт конфігурації"
#: src/components/routes/settings/config-yaml.tsx:48
msgid "Export your current systems configuration."
msgstr "Експортуйте поточну конфігурацію систем."
#: src/lib/utils.ts:38
msgid "Failed to authenticate"
msgstr "Не вдалося автентифікувати"
#: src/components/routes/settings/layout.tsx:39
#: src/components/routes/settings/notifications.tsx:62
msgid "Failed to save settings"
msgstr "Не вдалося зберегти налаштування"
#: src/components/routes/settings/notifications.tsx:188
msgid "Failed to send test notification"
msgstr "Не вдалося надіслати тестове сповіщення"
#: src/components/alerts/alerts-system.tsx:27
msgid "Failed to update alert"
msgstr "Не вдалося оновити сповіщення"
#: src/components/routes/system.tsx:539
#: src/components/systems-table/systems-table.tsx:324
msgid "Filter..."
msgstr "Фільтр..."
#: src/components/alerts/alerts-system.tsx:225
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
msgstr "Протягом <0>{min}</0> {min, plural, one {хвилини} other {хвилин}}"
#: src/components/login/auth-form.tsx:337
msgid "Forgot password?"
msgstr "Забули пароль?"
#. Context: General settings
#: src/components/routes/settings/general.tsx:33
#: src/components/routes/settings/layout.tsx:51
msgid "General"
msgstr "Загальні"
#: src/components/add-system.tsx:119
msgid "Host / IP"
msgstr "Хост / IP"
#: src/components/login/forgot-pass-form.tsx:93
msgid "If you've lost the password to your admin account, you may reset it using the following command."
msgstr "Якщо ви втратили пароль до свого адміністративного облікового запису, ви можете скинути його за допомогою наступної команди."
#: src/components/login/auth-form.tsx:16
msgid "Invalid email address."
msgstr "Неправильна адреса електронної пошти."
#. Linux kernel
#: src/components/routes/system.tsx:254
msgid "Kernel"
msgstr "Ядро"
#: src/components/routes/settings/general.tsx:45
msgid "Language"
msgstr "Мова"
#. Light theme
#: src/components/mode-toggle.tsx:16
msgid "Light"
msgstr "Світлий"
#: src/components/navbar.tsx:113
msgid "Log Out"
msgstr "Вийти"
#: src/components/login/login.tsx:17
msgid "Login"
msgstr "Увійти"
#: src/components/login/auth-form.tsx:42
#: src/components/login/forgot-pass-form.tsx:15
msgid "Login attempt failed"
msgstr "Спроба входу не вдалася"
#: src/components/command-palette.tsx:157
#: src/components/navbar.tsx:86
msgid "Logs"
msgstr "Журнали"
#: src/components/routes/settings/notifications.tsx:80
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
msgstr "Шукаєте, де створити сповіщення? Натисніть на іконки дзвінка <0/> в таблиці систем."
#: src/components/routes/settings/layout.tsx:85
msgid "Manage display and notification preferences."
msgstr "Керуйте параметрами відображення та сповіщень."
#. Chart select field. Please try to keep this short.
#: src/components/routes/system.tsx:571
msgid "Max 1 min"
msgstr "Макс 1 хв"
#: src/components/systems-table/systems-table.tsx:159
msgid "Memory"
msgstr "Пам'ять"
#: src/components/routes/system.tsx:397
#: src/lib/utils.ts:295
msgid "Memory Usage"
msgstr "Використання пам'яті"
#: src/components/routes/system.tsx:408
msgid "Memory usage of docker containers"
msgstr "Використання пам'яті контейнерами Docker"
#: src/components/add-system.tsx:113
msgid "Name"
msgstr "Ім'я"
#: src/components/systems-table/systems-table.tsx:173
msgid "Net"
msgstr "Мережа"
#: src/components/routes/system.tsx:453
msgid "Network traffic of docker containers"
msgstr "Мережевий трафік контейнерів Docker"
#: src/components/routes/system.tsx:438
msgid "Network traffic of public interfaces"
msgstr "Мережевий трафік публічних інтерфейсів"
#: src/components/command-palette.tsx:50
msgid "No results found."
msgstr "Результатів не знайдено."
#: src/components/systems-table/systems-table.tsx:400
msgid "No systems found."
msgstr "Систем не знайдено."
#: src/components/command-palette.tsx:111
#: src/components/routes/settings/layout.tsx:56
#: src/components/routes/settings/notifications.tsx:74
msgid "Notifications"
msgstr "Сповіщення"
#: src/components/login/auth-form.tsx:308
msgid "OAuth 2 / OIDC support"
msgstr "Підтримка OAuth 2 / OIDC"
#: src/components/routes/settings/config-yaml.tsx:61
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
msgstr "При кожному перезапуску системи в базі даних будуть оновлені, щоб відповідати системам, визначеним у файлі."
#: src/components/systems-table/systems-table.tsx:219
msgid "Open menu"
msgstr "Відкрити меню"
#: src/components/login/auth-form.tsx:227
msgid "Or continue with"
msgstr "Або продовжити з"
#: src/components/alerts/alert-button.tsx:109
msgid "Overwrite existing alerts"
msgstr "Перезаписати існуючі сповіщення"
#: src/components/command-palette.tsx:85
msgid "Page"
msgstr "Сторінка"
#: src/components/command-palette.tsx:72
msgid "Pages / Settings"
msgstr "Сторінки / Налаштування"
#: src/components/login/auth-form.tsx:171
#: src/components/login/auth-form.tsx:176
msgid "Password"
msgstr "Пароль"
#: src/components/login/auth-form.tsx:17
msgid "Password must be at least 10 characters."
msgstr "Пароль повинен містити щонайменше 10 символів."
#: src/components/login/forgot-pass-form.tsx:33
msgid "Password reset request received"
msgstr "Запит на скидання пароля отримано"
#: src/components/systems-table/systems-table.tsx:241
msgid "Pause"
msgstr "Пауза"
#: src/components/routes/settings/notifications.tsx:95
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
msgstr "Будь ласка, <0>налаштуйте SMTP сервер</0>, щоб забезпечити доставку сповіщень."
#: src/components/alerts/alerts-system.tsx:28
msgid "Please check logs for more details."
msgstr "Будь ласка, перевірте журнали для отримання додаткової інформації."
#: src/components/login/auth-form.tsx:43
#: src/components/login/forgot-pass-form.tsx:16
msgid "Please check your credentials and try again"
msgstr "Будь ласка, перевірте свої облікові дані та спробуйте ще раз"
#: src/components/login/login.tsx:34
msgid "Please create an admin account"
msgstr "Будь ласка, створіть адміністративний обліковий запис"
#: src/components/login/auth-form.tsx:257
msgid "Please enable pop-ups for this site"
msgstr "Будь ласка, увімкніть спливаючі вікна для цього сайту"
#: src/lib/utils.ts:39
msgid "Please log in again"
msgstr "Будь ласка, увійдіть знову"
#: src/components/login/auth-form.tsx:316
msgid "Please see <0>the documentation</0> for instructions."
msgstr "Будь ласка, перегляньте <0>документацію</0> для отримання інструкцій."
#: src/components/login/login.tsx:38
msgid "Please sign in to your account"
msgstr "Будь ласка, увійдіть у свій обліковий запис"
#: src/components/add-system.tsx:125
msgid "Port"
msgstr "Порт"
#: src/components/routes/system.tsx:398
msgid "Precise utilization at the recorded time"
msgstr "Точне використання в записаний час"
#: src/components/routes/settings/general.tsx:58
msgid "Preferred Language"
msgstr "Бажана мова"
#. Use 'Key' if your language requires many more characters
#: src/components/add-system.tsx:131
msgid "Public Key"
msgstr "Ключ"
#. Context is disk read
#: src/components/charts/area-chart.tsx:56
#: src/components/charts/area-chart.tsx:66
msgid "Read"
msgstr "Читання"
#. Context is network bytes received (download)
#: src/components/charts/area-chart.tsx:61
msgid "Received"
msgstr "Отримано"
#: src/components/login/forgot-pass-form.tsx:76
msgid "Reset Password"
msgstr "Скинути пароль"
#: src/components/systems-table/systems-table.tsx:236
msgid "Resume"
msgstr "Продовжити"
#: src/components/routes/settings/notifications.tsx:117
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
msgstr "Збережіть адресу, використовуючи клавішу Enter або кому. Залиште порожнім, щоб вимкнути сповіщення електронною поштою."
#: src/components/routes/settings/general.tsx:106
#: src/components/routes/settings/notifications.tsx:167
msgid "Save Settings"
msgstr "Зберегти налаштування"
#: src/components/navbar.tsx:142
msgid "Search"
msgstr "Пошук"
#: src/components/command-palette.tsx:47
msgid "Search for systems or settings..."
msgstr "Шукати системи або налаштування..."
#: src/components/alerts/alert-button.tsx:71
msgid "See <0>notification settings</0> to configure how you receive alerts."
msgstr "Перегляньте <0>налаштування сповіщень</0>, щоб налаштувати, як ви отримуєте сповіщення."
#. Context is network bytes sent (upload)
#: src/components/charts/area-chart.tsx:60
msgid "Sent"
msgstr "Відправлено"
#: src/components/routes/settings/general.tsx:100
msgid "Sets the default time range for charts when a system is viewed."
msgstr "Встановлює стандартний діапазон часу для графіків при перегляді системи."
#: src/components/command-palette.tsx:96
#: src/components/command-palette.tsx:99
#: src/components/command-palette.tsx:114
#: src/components/routes/settings/layout.tsx:71
#: src/components/routes/settings/layout.tsx:82
msgid "Settings"
msgstr "Налаштування"
#: src/components/routes/settings/layout.tsx:33
msgid "Settings saved"
msgstr "Налаштування збережено"
#: src/components/login/auth-form.tsx:215
msgid "Sign in"
msgstr "Увійти"
#: src/components/command-palette.tsx:201
msgid "SMTP settings"
msgstr "Налаштування SMTP"
#: src/lib/utils.ts:282
msgid "Status"
msgstr "Статус"
#: src/components/routes/system.tsx:467
msgid "Swap space used by the system"
msgstr "Область підкачки, використана системою"
#: src/components/routes/system.tsx:466
msgid "Swap Usage"
msgstr "Використання підкачки"
#. System theme
#: src/components/mode-toggle.tsx:26
#: src/components/systems-table/systems-table.tsx:110
#: src/components/systems-table/systems-table.tsx:121
msgid "System"
msgstr "Система"
#: src/components/navbar.tsx:78
msgid "Systems"
msgstr "Системи"
#: src/components/routes/settings/config-yaml.tsx:55
msgid "Systems may be managed in a <0>config.yml</0> file inside your data directory."
msgstr "Системи можуть керуватися у файлі <0>config.yml</0> у вашій директорії даних."
#: src/components/routes/system.tsx:477
#: src/lib/utils.ts:314
msgid "Temperature"
msgstr "Температура"
#: src/components/routes/system.tsx:478
msgid "Temperatures of system sensors"
msgstr "Температури датчиків системи"
#: src/components/routes/settings/notifications.tsx:211
msgid "Test <0>URL</0>"
msgstr "Тест <0>URL</0>"
#: src/components/routes/settings/notifications.tsx:182
msgid "Test notification sent"
msgstr "Тестове сповіщення надіслано"
#: src/components/add-system.tsx:104
msgid "The agent must be running on the system to connect. Copy the installation command for the agent below."
msgstr "Агент повинен працювати на системі для підключення. Скопіюйте команду встановлення для агента нижче."
#: src/components/add-system.tsx:95
msgid "The agent must be running on the system to connect. Copy the<0>docker-compose.yml</0> for the agent below."
msgstr "Агент повинен працювати на системі для підключення. Скопіюйте <0>docker-compose.yml</0> для агента нижче."
#: src/components/login/forgot-pass-form.tsx:98
msgid "Then log into the backend and reset your user account password in the users table."
msgstr "Потім увійдіть у бекенд і скиньте пароль вашого облікового запису користувача в таблиці користувачів."
#: src/components/systems-table/systems-table.tsx:264
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
msgstr "Цю дію не можна скасувати. Це назавжди видалить всі поточні записи для {name} з бази даних."
#: src/components/routes/system.tsx:507
msgid "Throughput of {extraFsName}"
msgstr "Пропускна здатність {extraFsName}"
#: src/components/routes/system.tsx:427
msgid "Throughput of root filesystem"
msgstr "Пропускна здатність кореневої файлової системи"
#: src/components/routes/settings/notifications.tsx:106
msgid "To email(s)"
msgstr "На електронну пошту"
#: src/components/routes/system.tsx:350
#: src/components/routes/system.tsx:363
msgid "Toggle grid"
msgstr "Перемкнути сітку"
#: src/components/mode-toggle.tsx:33
msgid "Toggle theme"
msgstr "Перемкнути тему"
#: src/lib/utils.ts:317
msgid "Triggers when any sensor exceeds a threshold"
msgstr "Спрацьовує, коли будь-який датчик перевищує поріг"
#: src/lib/utils.ts:310
msgid "Triggers when combined up/down exceeds a threshold"
msgstr "Спрацьовує, коли комбіноване підняття/падіння перевищує поріг"
#: src/lib/utils.ts:292
msgid "Triggers when CPU usage exceeds a threshold"
msgstr "Спрацьовує, коли використання ЦП перевищує поріг"
#: src/lib/utils.ts:298
msgid "Triggers when memory usage exceeds a threshold"
msgstr "Спрацьовує, коли використання пам'яті перевищує поріг"
#: src/lib/utils.ts:285
msgid "Triggers when status switches between up and down"
msgstr "Спрацьовує, коли статус перемикається між підняттям і падінням"
#: src/lib/utils.ts:304
msgid "Triggers when usage of any disk exceeds a threshold"
msgstr "Спрацьовує, коли використання будь-якого диска перевищує поріг"
#: src/components/systems-table/systems-table.tsx:320
msgid "Updated in real time. Click on a system to view information."
msgstr "Оновлюється в реальному часі. Натисніть на систему, щоб переглянути інформацію."
#: src/components/routes/system.tsx:253
msgid "Uptime"
msgstr "Час роботи"
#: src/components/routes/system.tsx:494
msgid "Usage"
msgstr "Використання"
#: src/components/routes/system.tsx:415
msgid "Usage of root partition"
msgstr "Використання кореневого розділу"
#: src/components/charts/mem-chart.tsx:65
#: src/components/charts/swap-chart.tsx:56
msgid "Used"
msgstr "Використано"
#: src/components/login/auth-form.tsx:138
msgid "username"
msgstr "ім'я користувача"
#: src/components/login/auth-form.tsx:131
msgid "Username"
msgstr "Ім'я користувача"
#: src/components/command-palette.tsx:143
#: src/components/navbar.tsx:70
msgid "Users"
msgstr "Користувачі"
#: src/components/routes/system.tsx:603
msgid "Waiting for enough records to display"
msgstr "Очікування достатньої кількості записів для відображення"
#: src/components/routes/settings/general.tsx:48
msgid "Want to help us make our translations even better? Check out <0>Crowdin</0> for more details."
msgstr "Хочете допомогти нам зробити наші переклади ще кращими? Перегляньте <0>Crowdin</0> для отримання додаткової інформації."
#: src/components/routes/settings/notifications.tsx:124
msgid "Webhook / Push notifications"
msgstr "Webhook / Push сповіщення"
#. Context is disk write
#: src/components/charts/area-chart.tsx:55
#: src/components/charts/area-chart.tsx:65
msgid "Write"
msgstr "Запис"
#: src/components/routes/settings/layout.tsx:61
msgid "YAML Config"
msgstr "Конфігурація YAML"
#: src/components/routes/settings/config-yaml.tsx:45
msgid "YAML Configuration"
msgstr "Конфігурація YAML"
#: src/components/routes/settings/layout.tsx:34
msgid "Your user settings have been updated."
msgstr "Ваші налаштування користувача були оновлені."

View File

@@ -0,0 +1,808 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2024-11-01 11:30-0400\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: vi\n"
"Project-Id-Version: beszel\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-11-04 20:47\n"
"Last-Translator: \n"
"Language-Team: Vietnamese\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Crowdin-Project: beszel\n"
"X-Crowdin-Project-ID: 733311\n"
"X-Crowdin-Language: vi\n"
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 16\n"
#: src/components/routes/system.tsx:242
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# ngày} other {# ngày}}"
#: src/components/routes/system.tsx:240
msgid "{hours, plural, one {# hour} other {# hours}}"
msgstr "{hours, plural, one {# giờ} other {# giờ}}"
#: src/lib/utils.ts:139
msgid "1 hour"
msgstr "1 giờ"
#: src/lib/utils.ts:162
msgid "1 week"
msgstr "1 tuần"
#: src/lib/utils.ts:147
msgid "12 hours"
msgstr "12 giờ"
#: src/lib/utils.ts:155
msgid "24 hours"
msgstr "24 giờ"
#: src/lib/utils.ts:170
msgid "30 days"
msgstr "30 ngày"
#. Table column
#: src/components/systems-table/systems-table.tsx:207
msgid "Actions"
msgstr "Hành động"
#: src/components/routes/home.tsx:62
msgid "Active Alerts"
msgstr "Cảnh báo hoạt động"
#: src/components/add-system.tsx:74
msgid "Add <0>System</0>"
msgstr "Thêm <0>Hệ thống</0>"
#: src/components/add-system.tsx:83
msgid "Add New System"
msgstr "Thêm Hệ thống Mới"
#: src/components/add-system.tsx:167
#: src/components/add-system.tsx:178
msgid "Add system"
msgstr "Thêm hệ thống"
#: src/components/routes/settings/notifications.tsx:156
msgid "Add URL"
msgstr "Thêm URL"
#: src/components/routes/settings/general.tsx:81
msgid "Adjust display options for charts."
msgstr "Điều chỉnh tùy chọn hiển thị cho biểu đồ."
#: src/components/command-palette.tsx:133
#: src/components/command-palette.tsx:146
#: src/components/command-palette.tsx:160
#: src/components/command-palette.tsx:174
#: src/components/command-palette.tsx:189
#: src/components/command-palette.tsx:204
msgid "Admin"
msgstr "Quản trị viên"
#: src/components/systems-table/systems-table.tsx:186
msgid "Agent"
msgstr "Tác nhân"
#: src/components/alerts/alert-button.tsx:32
#: src/components/alerts/alert-button.tsx:68
msgid "Alerts"
msgstr "Cảnh báo"
#: src/components/alerts/alert-button.tsx:88
#: src/components/systems-table/systems-table.tsx:317
msgid "All Systems"
msgstr "Tất cả Hệ thống"
#: src/components/systems-table/systems-table.tsx:261
msgid "Are you sure you want to delete {name}?"
msgstr "Bạn có chắc chắn muốn xóa {name} không?"
#: src/components/command-palette.tsx:186
#: src/components/navbar.tsx:102
msgid "Auth Providers"
msgstr "Nhà cung cấp Xác thực"
#: src/components/copy-to-clipboard.tsx:16
msgid "Automatic copy requires a secure context."
msgstr "Sao chép tự động yêu cầu một ngữ cảnh an toàn."
#: src/components/routes/system.tsx:568
msgid "Average"
msgstr "Trung bình"
#: src/components/routes/system.tsx:387
msgid "Average CPU utilization of containers"
msgstr "Sử dụng CPU trung bình của các container"
#: src/components/alerts/alerts-system.tsx:204
msgid "Average exceeds <0>{value}{0}</0>"
msgstr "Trung bình vượt quá <0>{value}{0}</0>"
#: src/components/routes/system.tsx:376
msgid "Average system-wide CPU utilization"
msgstr "Sử dụng CPU trung bình toàn hệ thống"
#: src/components/command-palette.tsx:171
#: src/components/navbar.tsx:94
msgid "Backups"
msgstr "Sao lưu"
#: src/components/routes/system.tsx:436
#: src/lib/utils.ts:307
msgid "Bandwidth"
msgstr "Băng thông"
#: src/components/login/auth-form.tsx:313
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
msgstr "Beszel hỗ trợ OpenID Connect và nhiều nhà cung cấp xác thực OAuth2."
#: src/components/routes/settings/notifications.tsx:127
msgid "Beszel uses <0>Shoutrrr</0> to integrate with popular notification services."
msgstr "Beszel sử dụng <0>Shoutrrr</0> để tích hợp với các dịch vụ thông báo phổ biến."
#: src/components/add-system.tsx:88
msgid "Binary"
msgstr "Nhị phân"
#: src/components/charts/mem-chart.tsx:89
msgid "Cache / Buffers"
msgstr "Bộ nhớ đệm / Bộ đệm"
#: src/components/systems-table/systems-table.tsx:272
msgid "Cancel"
msgstr "Hủy bỏ"
#: src/components/routes/settings/config-yaml.tsx:68
msgid "Caution - potential data loss"
msgstr "Cẩn thận - có thể mất dữ liệu"
#: src/components/routes/settings/general.tsx:36
msgid "Change general application options."
msgstr "Thay đổi các tùy chọn ứng dụng chung."
#: src/components/routes/settings/general.tsx:78
msgid "Chart options"
msgstr "Tùy chọn biểu đồ"
#: src/components/login/forgot-pass-form.tsx:34
msgid "Check {email} for a reset link."
msgstr "Kiểm tra {email} để lấy liên kết đặt lại."
#: src/components/routes/settings/layout.tsx:40
msgid "Check logs for more details."
msgstr "Kiểm tra nhật ký để biết thêm chi tiết."
#: src/components/routes/settings/notifications.tsx:183
msgid "Check your notification service"
msgstr "Kiểm tra dịch vụ thông báo của bạn"
#: src/components/add-system.tsx:153
msgid "Click to copy"
msgstr "Nhấp để sao chép"
#. Context: table columns
#: src/components/systems-table/systems-table.tsx:328
msgid "Columns"
msgstr "Cột"
#: src/components/login/forgot-pass-form.tsx:83
#: src/components/login/forgot-pass-form.tsx:89
msgid "Command line instructions"
msgstr "Hướng dẫn dòng lệnh"
#: src/components/routes/settings/notifications.tsx:77
msgid "Configure how you receive alert notifications."
msgstr "Cấu hình cách bạn nhận thông báo cảnh báo."
#: src/components/login/auth-form.tsx:189
#: src/components/login/auth-form.tsx:194
msgid "Confirm password"
msgstr "Xác nhận mật khẩu"
#: src/components/systems-table/systems-table.tsx:278
msgid "Continue"
msgstr "Tiếp tục"
#: src/lib/utils.ts:25
msgid "Copied to clipboard"
msgstr "Đã sao chép vào clipboard"
#: src/components/add-system.tsx:164
msgid "Copy"
msgstr "Sao chép"
#: src/components/systems-table/systems-table.tsx:247
msgid "Copy host"
msgstr "Sao chép máy chủ"
#: src/components/add-system.tsx:175
msgid "Copy Linux command"
msgstr "Sao chép lệnh Linux"
#: src/components/copy-to-clipboard.tsx:13
msgid "Copy text"
msgstr "Sao chép văn bản"
#: src/components/systems-table/systems-table.tsx:152
msgid "CPU"
msgstr "CPU"
#: src/components/charts/area-chart.tsx:52
#: src/components/routes/system.tsx:375
#: src/lib/utils.ts:289
msgid "CPU Usage"
msgstr "Sử dụng CPU"
#: src/components/login/auth-form.tsx:215
msgid "Create account"
msgstr "Tạo tài khoản"
#. Dark theme
#: src/components/mode-toggle.tsx:21
msgid "Dark"
msgstr "Tối"
#: src/components/command-palette.tsx:82
#: src/components/routes/home.tsx:35
msgid "Dashboard"
msgstr "Bảng điều khiển"
#: src/components/routes/settings/general.tsx:85
msgid "Default time period"
msgstr "Thời gian mặc định"
#: src/components/systems-table/systems-table.tsx:253
msgid "Delete"
msgstr "Xóa"
#: src/components/systems-table/systems-table.tsx:166
msgid "Disk"
msgstr "Đĩa"
#: src/components/routes/system.tsx:426
msgid "Disk I/O"
msgstr "Đĩa I/O"
#: src/components/charts/disk-chart.tsx:74
#: src/components/routes/system.tsx:415
#: src/lib/utils.ts:301
msgid "Disk Usage"
msgstr "Sử dụng Đĩa"
#: src/components/routes/system.tsx:495
msgid "Disk usage of {extraFsName}"
msgstr "Sử dụng đĩa của {extraFsName}"
#: src/components/routes/system.tsx:386
msgid "Docker CPU Usage"
msgstr "Sử dụng CPU Docker"
#: src/components/routes/system.tsx:407
msgid "Docker Memory Usage"
msgstr "Sử dụng Bộ nhớ Docker"
#: src/components/routes/system.tsx:452
msgid "Docker Network I/O"
msgstr "Mạng I/O Docker"
#: src/components/command-palette.tsx:125
msgid "Documentation"
msgstr "Tài liệu"
#: src/components/login/auth-form.tsx:158
msgid "email"
msgstr "email"
#: src/components/login/auth-form.tsx:152
#: src/components/login/forgot-pass-form.tsx:53
msgid "Email"
msgstr "Email"
#: src/components/routes/settings/notifications.tsx:91
msgid "Email notifications"
msgstr "Thông báo email"
#: src/components/login/login.tsx:36
msgid "Enter email address to reset password"
msgstr "Nhập địa chỉ email để đặt lại mật khẩu"
#: src/components/routes/settings/notifications.tsx:111
msgid "Enter email address..."
msgstr "Nhập địa chỉ email..."
#: src/components/login/auth-form.tsx:256
#: src/components/routes/settings/config-yaml.tsx:28
#: src/components/routes/settings/notifications.tsx:187
msgid "Error"
msgstr "Lỗi"
#: src/components/routes/home.tsx:81
msgid "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
msgstr "Vượt quá {0}{1} trong {2, plural, one {# phút} other {# phút}} qua"
#: src/components/routes/settings/config-yaml.tsx:72
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
msgstr "Các hệ thống hiện có không được định nghĩa trong <0>config.yml</0> sẽ bị xóa. Vui lòng sao lưu thường xuyên."
#: src/components/routes/settings/config-yaml.tsx:93
msgid "Export configuration"
msgstr "Xuất cấu hình"
#: src/components/routes/settings/config-yaml.tsx:48
msgid "Export your current systems configuration."
msgstr "Xuất cấu hình hệ thống hiện tại của bạn."
#: src/lib/utils.ts:38
msgid "Failed to authenticate"
msgstr "Xác thực thất bại"
#: src/components/routes/settings/layout.tsx:39
#: src/components/routes/settings/notifications.tsx:62
msgid "Failed to save settings"
msgstr "Lưu cài đặt thất bại"
#: src/components/routes/settings/notifications.tsx:188
msgid "Failed to send test notification"
msgstr "Gửi thông báo thử nghiệm thất bại"
#: src/components/alerts/alerts-system.tsx:27
msgid "Failed to update alert"
msgstr "Cập nhật cảnh báo thất bại"
#: src/components/routes/system.tsx:539
#: src/components/systems-table/systems-table.tsx:324
msgid "Filter..."
msgstr "Lọc..."
#: src/components/alerts/alerts-system.tsx:225
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
msgstr "Trong <0>{min}</0> {min, plural, one {phút} other {phút}}"
#: src/components/login/auth-form.tsx:337
msgid "Forgot password?"
msgstr "Quên mật khẩu?"
#. Context: General settings
#: src/components/routes/settings/general.tsx:33
#: src/components/routes/settings/layout.tsx:51
msgid "General"
msgstr "Chung"
#: src/components/add-system.tsx:119
msgid "Host / IP"
msgstr "Máy chủ / IP"
#: src/components/login/forgot-pass-form.tsx:93
msgid "If you've lost the password to your admin account, you may reset it using the following command."
msgstr "Nếu bạn đã mất mật khẩu cho tài khoản quản trị viên của mình, bạn có thể đặt lại bằng cách sử dụng lệnh sau."
#: src/components/login/auth-form.tsx:16
msgid "Invalid email address."
msgstr "Địa chỉ email không hợp lệ."
#. Linux kernel
#: src/components/routes/system.tsx:254
msgid "Kernel"
msgstr "Nhân"
#: src/components/routes/settings/general.tsx:45
msgid "Language"
msgstr "Ngôn ngữ"
#. Light theme
#: src/components/mode-toggle.tsx:16
msgid "Light"
msgstr "Sáng"
#: src/components/navbar.tsx:113
msgid "Log Out"
msgstr "Đăng xuất"
#: src/components/login/login.tsx:17
msgid "Login"
msgstr "Đăng nhập"
#: src/components/login/auth-form.tsx:42
#: src/components/login/forgot-pass-form.tsx:15
msgid "Login attempt failed"
msgstr "Nỗ lực đăng nhập thất bại"
#: src/components/command-palette.tsx:157
#: src/components/navbar.tsx:86
msgid "Logs"
msgstr "Nhật ký"
#: src/components/routes/settings/notifications.tsx:80
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
msgstr "Thay vào đó, bạn đang tìm nơi để tạo cảnh báo? Nhấp vào biểu tượng chuông <0/> trong bảng hệ thống."
#: src/components/routes/settings/layout.tsx:85
msgid "Manage display and notification preferences."
msgstr "Quản lý tùy chọn hiển thị và thông báo."
#. Chart select field. Please try to keep this short.
#: src/components/routes/system.tsx:571
msgid "Max 1 min"
msgstr "Tối đa 1 phút"
#: src/components/systems-table/systems-table.tsx:159
msgid "Memory"
msgstr "Bộ nhớ"
#: src/components/routes/system.tsx:397
#: src/lib/utils.ts:295
msgid "Memory Usage"
msgstr "Sử dụng Bộ nhớ"
#: src/components/routes/system.tsx:408
msgid "Memory usage of docker containers"
msgstr "Sử dụng bộ nhớ của các container Docker"
#: src/components/add-system.tsx:113
msgid "Name"
msgstr "Tên"
#: src/components/systems-table/systems-table.tsx:173
msgid "Net"
msgstr "Mạng"
#: src/components/routes/system.tsx:453
msgid "Network traffic of docker containers"
msgstr "Lưu lượng mạng của các container Docker"
#: src/components/routes/system.tsx:438
msgid "Network traffic of public interfaces"
msgstr "Lưu lượng mạng của các giao diện công cộng"
#: src/components/command-palette.tsx:50
msgid "No results found."
msgstr "Không tìm thấy kết quả."
#: src/components/systems-table/systems-table.tsx:400
msgid "No systems found."
msgstr "Không tìm thấy hệ thống."
#: src/components/command-palette.tsx:111
#: src/components/routes/settings/layout.tsx:56
#: src/components/routes/settings/notifications.tsx:74
msgid "Notifications"
msgstr "Thông báo"
#: src/components/login/auth-form.tsx:308
msgid "OAuth 2 / OIDC support"
msgstr "Hỗ trợ OAuth 2 / OIDC"
#: src/components/routes/settings/config-yaml.tsx:61
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
msgstr "Mỗi khi khởi động lại, các hệ thống trong cơ sở dữ liệu sẽ được cập nhật để khớp với các hệ thống được định nghĩa trong tệp."
#: src/components/systems-table/systems-table.tsx:219
msgid "Open menu"
msgstr "Mở menu"
#: src/components/login/auth-form.tsx:227
msgid "Or continue with"
msgstr "Hoặc tiếp tục với"
#: src/components/alerts/alert-button.tsx:109
msgid "Overwrite existing alerts"
msgstr "Ghi đè các cảnh báo hiện có"
#: src/components/command-palette.tsx:85
msgid "Page"
msgstr "Trang"
#: src/components/command-palette.tsx:72
msgid "Pages / Settings"
msgstr "Trang / Cài đặt"
#: src/components/login/auth-form.tsx:171
#: src/components/login/auth-form.tsx:176
msgid "Password"
msgstr "Mật khẩu"
#: src/components/login/auth-form.tsx:17
msgid "Password must be at least 10 characters."
msgstr "Mật khẩu phải có ít nhất 10 ký tự."
#: src/components/login/forgot-pass-form.tsx:33
msgid "Password reset request received"
msgstr "Yêu cầu đặt lại mật khẩu đã được nhận"
#: src/components/systems-table/systems-table.tsx:241
msgid "Pause"
msgstr "Tạm dừng"
#: src/components/routes/settings/notifications.tsx:95
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
msgstr "Vui lòng <0>cấu hình máy chủ SMTP</0> để đảm bảo cảnh báo được gửi đi."
#: src/components/alerts/alerts-system.tsx:28
msgid "Please check logs for more details."
msgstr "Vui lòng kiểm tra nhật ký để biết thêm chi tiết."
#: src/components/login/auth-form.tsx:43
#: src/components/login/forgot-pass-form.tsx:16
msgid "Please check your credentials and try again"
msgstr "Vui lòng kiểm tra thông tin đăng nhập của bạn và thử lại"
#: src/components/login/login.tsx:34
msgid "Please create an admin account"
msgstr "Vui lòng tạo một tài khoản quản trị viên"
#: src/components/login/auth-form.tsx:257
msgid "Please enable pop-ups for this site"
msgstr "Vui lòng bật cửa sổ bật lên cho trang web này"
#: src/lib/utils.ts:39
msgid "Please log in again"
msgstr "Vui lòng đăng nhập lại"
#: src/components/login/auth-form.tsx:316
msgid "Please see <0>the documentation</0> for instructions."
msgstr "Vui lòng xem <0>tài liệu</0> để biết hướng dẫn."
#: src/components/login/login.tsx:38
msgid "Please sign in to your account"
msgstr "Vui lòng đăng nhập vào tài khoản của bạn"
#: src/components/add-system.tsx:125
msgid "Port"
msgstr "Cổng"
#: src/components/routes/system.tsx:398
msgid "Precise utilization at the recorded time"
msgstr "Sử dụng chính xác tại thời điểm ghi nhận"
#: src/components/routes/settings/general.tsx:58
msgid "Preferred Language"
msgstr "Ngôn ngữ Ưa thích"
#. Use 'Key' if your language requires many more characters
#: src/components/add-system.tsx:131
msgid "Public Key"
msgstr "Khóa"
#. Context is disk read
#: src/components/charts/area-chart.tsx:56
#: src/components/charts/area-chart.tsx:66
msgid "Read"
msgstr "Đọc"
#. Context is network bytes received (download)
#: src/components/charts/area-chart.tsx:61
msgid "Received"
msgstr "Đã nhận"
#: src/components/login/forgot-pass-form.tsx:76
msgid "Reset Password"
msgstr "Đặt lại Mật khẩu"
#: src/components/systems-table/systems-table.tsx:236
msgid "Resume"
msgstr "Tiếp tục"
#: src/components/routes/settings/notifications.tsx:117
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
msgstr "Lưu địa chỉ bằng cách sử dụng phím enter hoặc dấu phẩy. Để trống để vô hiệu hóa thông báo email."
#: src/components/routes/settings/general.tsx:106
#: src/components/routes/settings/notifications.tsx:167
msgid "Save Settings"
msgstr "Lưu Cài đặt"
#: src/components/navbar.tsx:142
msgid "Search"
msgstr "Tìm kiếm"
#: src/components/command-palette.tsx:47
msgid "Search for systems or settings..."
msgstr "Tìm kiếm hệ thống hoặc cài đặt..."
#: src/components/alerts/alert-button.tsx:71
msgid "See <0>notification settings</0> to configure how you receive alerts."
msgstr "Xem <0>cài đặt thông báo</0> để cấu hình cách bạn nhận cảnh báo."
#. Context is network bytes sent (upload)
#: src/components/charts/area-chart.tsx:60
msgid "Sent"
msgstr "Đã gửi"
#: src/components/routes/settings/general.tsx:100
msgid "Sets the default time range for charts when a system is viewed."
msgstr "Đặt phạm vi thời gian mặc định cho biểu đồ khi một hệ thống được xem."
#: src/components/command-palette.tsx:96
#: src/components/command-palette.tsx:99
#: src/components/command-palette.tsx:114
#: src/components/routes/settings/layout.tsx:71
#: src/components/routes/settings/layout.tsx:82
msgid "Settings"
msgstr "Cài đặt"
#: src/components/routes/settings/layout.tsx:33
msgid "Settings saved"
msgstr "Cài đặt đã được lưu"
#: src/components/login/auth-form.tsx:215
msgid "Sign in"
msgstr "Đăng nhập"
#: src/components/command-palette.tsx:201
msgid "SMTP settings"
msgstr "Cài đặt SMTP"
#: src/lib/utils.ts:282
msgid "Status"
msgstr "Trạng thái"
#: src/components/routes/system.tsx:467
msgid "Swap space used by the system"
msgstr "Không gian hoán đổi được sử dụng bởi hệ thống"
#: src/components/routes/system.tsx:466
msgid "Swap Usage"
msgstr "Sử dụng Hoán đổi"
#. System theme
#: src/components/mode-toggle.tsx:26
#: src/components/systems-table/systems-table.tsx:110
#: src/components/systems-table/systems-table.tsx:121
msgid "System"
msgstr "Hệ thống"
#: src/components/navbar.tsx:78
msgid "Systems"
msgstr "Các hệ thống"
#: src/components/routes/settings/config-yaml.tsx:55
msgid "Systems may be managed in a <0>config.yml</0> file inside your data directory."
msgstr "Các hệ thống có thể được quản lý trong tệp <0>config.yml</0> bên trong thư mục dữ liệu của bạn."
#: src/components/routes/system.tsx:477
#: src/lib/utils.ts:314
msgid "Temperature"
msgstr "Nhiệt độ"
#: src/components/routes/system.tsx:478
msgid "Temperatures of system sensors"
msgstr "Nhiệt độ của các cảm biến hệ thống"
#: src/components/routes/settings/notifications.tsx:211
msgid "Test <0>URL</0>"
msgstr "Kiểm tra <0>URL</0>"
#: src/components/routes/settings/notifications.tsx:182
msgid "Test notification sent"
msgstr "Thông báo thử nghiệm đã được gửi"
#: src/components/add-system.tsx:104
msgid "The agent must be running on the system to connect. Copy the installation command for the agent below."
msgstr "Tác nhân phải đang chạy trên hệ thống để kết nối. Sao chép lệnh cài đặt cho tác nhân bên dưới."
#: src/components/add-system.tsx:95
msgid "The agent must be running on the system to connect. Copy the<0>docker-compose.yml</0> for the agent below."
msgstr "Tác nhân phải đang chạy trên hệ thống để kết nối. Sao chép <0>docker-compose.yml</0> cho tác nhân bên dưới."
#: src/components/login/forgot-pass-form.tsx:98
msgid "Then log into the backend and reset your user account password in the users table."
msgstr "Sau đó đăng nhập vào backend và đặt lại mật khẩu tài khoản người dùng của bạn trong bảng người dùng."
#: src/components/systems-table/systems-table.tsx:264
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
msgstr "Hành động này không thể hoàn tác. Điều này sẽ xóa vĩnh viễn tất cả các bản ghi hiện tại cho {name} khỏi cơ sở dữ liệu."
#: src/components/routes/system.tsx:507
msgid "Throughput of {extraFsName}"
msgstr "Thông lượng của {extraFsName}"
#: src/components/routes/system.tsx:427
msgid "Throughput of root filesystem"
msgstr "Thông lượng của hệ thống tệp gốc"
#: src/components/routes/settings/notifications.tsx:106
msgid "To email(s)"
msgstr "Đến email(s)"
#: src/components/routes/system.tsx:350
#: src/components/routes/system.tsx:363
msgid "Toggle grid"
msgstr "Chuyển đổi lưới"
#: src/components/mode-toggle.tsx:33
msgid "Toggle theme"
msgstr "Chuyển đổi chủ đề"
#: src/lib/utils.ts:317
msgid "Triggers when any sensor exceeds a threshold"
msgstr "Kích hoạt khi bất kỳ cảm biến nào vượt quá ngưỡng"
#: src/lib/utils.ts:310
msgid "Triggers when combined up/down exceeds a threshold"
msgstr "Kích hoạt khi kết hợp lên/xuống vượt quá ngưỡng"
#: src/lib/utils.ts:292
msgid "Triggers when CPU usage exceeds a threshold"
msgstr "Kích hoạt khi sử dụng CPU vượt quá ngưỡng"
#: src/lib/utils.ts:298
msgid "Triggers when memory usage exceeds a threshold"
msgstr "Kích hoạt khi sử dụng bộ nhớ vượt quá ngưỡng"
#: src/lib/utils.ts:285
msgid "Triggers when status switches between up and down"
msgstr "Kích hoạt khi trạng thái chuyển đổi giữa lên và xuống"
#: src/lib/utils.ts:304
msgid "Triggers when usage of any disk exceeds a threshold"
msgstr "Kích hoạt khi sử dụng bất kỳ đĩa nào vượt quá ngưỡng"
#: src/components/systems-table/systems-table.tsx:320
msgid "Updated in real time. Click on a system to view information."
msgstr "Cập nhật theo thời gian thực. Nhấp vào một hệ thống để xem thông tin."
#: src/components/routes/system.tsx:253
msgid "Uptime"
msgstr "Thời gian hoạt động"
#: src/components/routes/system.tsx:494
msgid "Usage"
msgstr "Sử dụng"
#: src/components/routes/system.tsx:415
msgid "Usage of root partition"
msgstr "Sử dụng phân vùng gốc"
#: src/components/charts/mem-chart.tsx:65
#: src/components/charts/swap-chart.tsx:56
msgid "Used"
msgstr "Đã sử dụng"
#: src/components/login/auth-form.tsx:138
msgid "username"
msgstr "tên người dùng"
#: src/components/login/auth-form.tsx:131
msgid "Username"
msgstr "Tên người dùng"
#: src/components/command-palette.tsx:143
#: src/components/navbar.tsx:70
msgid "Users"
msgstr "Người dùng"
#: src/components/routes/system.tsx:603
msgid "Waiting for enough records to display"
msgstr "Đang chờ đủ bản ghi để hiển thị"
#: src/components/routes/settings/general.tsx:48
msgid "Want to help us make our translations even better? Check out <0>Crowdin</0> for more details."
msgstr "Muốn giúp chúng tôi cải thiện bản dịch của mình? Xem <0>Crowdin</0> để biết thêm chi tiết."
#: src/components/routes/settings/notifications.tsx:124
msgid "Webhook / Push notifications"
msgstr "Thông báo Webhook / Push"
#. Context is disk write
#: src/components/charts/area-chart.tsx:55
#: src/components/charts/area-chart.tsx:65
msgid "Write"
msgstr "Ghi"
#: src/components/routes/settings/layout.tsx:61
msgid "YAML Config"
msgstr "Cấu hình YAML"
#: src/components/routes/settings/config-yaml.tsx:45
msgid "YAML Configuration"
msgstr "Cấu hình YAML"
#: src/components/routes/settings/layout.tsx:34
msgid "Your user settings have been updated."
msgstr "Cài đặt người dùng của bạn đã được cập nhật."

View File

@@ -0,0 +1,808 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2024-11-01 11:30-0400\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: zh\n"
"Project-Id-Version: beszel\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-11-04 20:46\n"
"Last-Translator: \n"
"Language-Team: Chinese Simplified\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Crowdin-Project: beszel\n"
"X-Crowdin-Project-ID: 733311\n"
"X-Crowdin-Language: zh-CN\n"
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 16\n"
#: src/components/routes/system.tsx:242
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# 天} other {# 天}}"
#: src/components/routes/system.tsx:240
msgid "{hours, plural, one {# hour} other {# hours}}"
msgstr "{hours, plural, one {# 小时} other {# 小时}}"
#: src/lib/utils.ts:139
msgid "1 hour"
msgstr "1小时"
#: src/lib/utils.ts:162
msgid "1 week"
msgstr "1周"
#: src/lib/utils.ts:147
msgid "12 hours"
msgstr "12小时"
#: src/lib/utils.ts:155
msgid "24 hours"
msgstr "24小时"
#: src/lib/utils.ts:170
msgid "30 days"
msgstr "30天"
#. Table column
#: src/components/systems-table/systems-table.tsx:207
msgid "Actions"
msgstr "操作"
#: src/components/routes/home.tsx:62
msgid "Active Alerts"
msgstr "活动警报"
#: src/components/add-system.tsx:74
msgid "Add <0>System</0>"
msgstr "添加<0>系统</0>"
#: src/components/add-system.tsx:83
msgid "Add New System"
msgstr "添加新系统"
#: src/components/add-system.tsx:167
#: src/components/add-system.tsx:178
msgid "Add system"
msgstr "添加系统"
#: src/components/routes/settings/notifications.tsx:156
msgid "Add URL"
msgstr "添加URL"
#: src/components/routes/settings/general.tsx:81
msgid "Adjust display options for charts."
msgstr "调整图表的显示选项。"
#: src/components/command-palette.tsx:133
#: src/components/command-palette.tsx:146
#: src/components/command-palette.tsx:160
#: src/components/command-palette.tsx:174
#: src/components/command-palette.tsx:189
#: src/components/command-palette.tsx:204
msgid "Admin"
msgstr "管理员"
#: src/components/systems-table/systems-table.tsx:186
msgid "Agent"
msgstr "代理"
#: src/components/alerts/alert-button.tsx:32
#: src/components/alerts/alert-button.tsx:68
msgid "Alerts"
msgstr "警报"
#: src/components/alerts/alert-button.tsx:88
#: src/components/systems-table/systems-table.tsx:317
msgid "All Systems"
msgstr "所有系统"
#: src/components/systems-table/systems-table.tsx:261
msgid "Are you sure you want to delete {name}?"
msgstr "您确定要删除{name}吗?"
#: src/components/command-palette.tsx:186
#: src/components/navbar.tsx:102
msgid "Auth Providers"
msgstr "认证提供者"
#: src/components/copy-to-clipboard.tsx:16
msgid "Automatic copy requires a secure context."
msgstr "自动复制需要安全的上下文。"
#: src/components/routes/system.tsx:568
msgid "Average"
msgstr "平均"
#: src/components/routes/system.tsx:387
msgid "Average CPU utilization of containers"
msgstr "容器的平均CPU使用率"
#: src/components/alerts/alerts-system.tsx:204
msgid "Average exceeds <0>{value}{0}</0>"
msgstr "平均值超过<0>{value}{0}</0>"
#: src/components/routes/system.tsx:376
msgid "Average system-wide CPU utilization"
msgstr "系统范围内的平均CPU使用率"
#: src/components/command-palette.tsx:171
#: src/components/navbar.tsx:94
msgid "Backups"
msgstr "备份"
#: src/components/routes/system.tsx:436
#: src/lib/utils.ts:307
msgid "Bandwidth"
msgstr "带宽"
#: src/components/login/auth-form.tsx:313
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
msgstr "Beszel支持OpenID Connect和许多OAuth2认证提供者。"
#: src/components/routes/settings/notifications.tsx:127
msgid "Beszel uses <0>Shoutrrr</0> to integrate with popular notification services."
msgstr "Beszel使用<0>Shoutrrr</0>与流行的通知服务集成。"
#: src/components/add-system.tsx:88
msgid "Binary"
msgstr "二进制"
#: src/components/charts/mem-chart.tsx:89
msgid "Cache / Buffers"
msgstr "缓存/缓冲区"
#: src/components/systems-table/systems-table.tsx:272
msgid "Cancel"
msgstr "取消"
#: src/components/routes/settings/config-yaml.tsx:68
msgid "Caution - potential data loss"
msgstr "注意 - 可能的数据丢失"
#: src/components/routes/settings/general.tsx:36
msgid "Change general application options."
msgstr "更改一般应用程序选项。"
#: src/components/routes/settings/general.tsx:78
msgid "Chart options"
msgstr "图表选项"
#: src/components/login/forgot-pass-form.tsx:34
msgid "Check {email} for a reset link."
msgstr "检查{email}以获取重置链接。"
#: src/components/routes/settings/layout.tsx:40
msgid "Check logs for more details."
msgstr "检查日志以获取更多详细信息。"
#: src/components/routes/settings/notifications.tsx:183
msgid "Check your notification service"
msgstr "检查您的通知服务"
#: src/components/add-system.tsx:153
msgid "Click to copy"
msgstr "点击复制"
#. Context: table columns
#: src/components/systems-table/systems-table.tsx:328
msgid "Columns"
msgstr "列"
#: src/components/login/forgot-pass-form.tsx:83
#: src/components/login/forgot-pass-form.tsx:89
msgid "Command line instructions"
msgstr "命令行说明"
#: src/components/routes/settings/notifications.tsx:77
msgid "Configure how you receive alert notifications."
msgstr "配置您接收警报通知的方式。"
#: src/components/login/auth-form.tsx:189
#: src/components/login/auth-form.tsx:194
msgid "Confirm password"
msgstr "确认密码"
#: src/components/systems-table/systems-table.tsx:278
msgid "Continue"
msgstr "继续"
#: src/lib/utils.ts:25
msgid "Copied to clipboard"
msgstr "已复制到剪贴板"
#: src/components/add-system.tsx:164
msgid "Copy"
msgstr "复制"
#: src/components/systems-table/systems-table.tsx:247
msgid "Copy host"
msgstr "复制主机"
#: src/components/add-system.tsx:175
msgid "Copy Linux command"
msgstr "复制Linux命令"
#: src/components/copy-to-clipboard.tsx:13
msgid "Copy text"
msgstr "复制文本"
#: src/components/systems-table/systems-table.tsx:152
msgid "CPU"
msgstr "CPU"
#: src/components/charts/area-chart.tsx:52
#: src/components/routes/system.tsx:375
#: src/lib/utils.ts:289
msgid "CPU Usage"
msgstr "CPU使用率"
#: src/components/login/auth-form.tsx:215
msgid "Create account"
msgstr "创建账户"
#. Dark theme
#: src/components/mode-toggle.tsx:21
msgid "Dark"
msgstr "深色"
#: src/components/command-palette.tsx:82
#: src/components/routes/home.tsx:35
msgid "Dashboard"
msgstr "仪表板"
#: src/components/routes/settings/general.tsx:85
msgid "Default time period"
msgstr "默认时间段"
#: src/components/systems-table/systems-table.tsx:253
msgid "Delete"
msgstr "删除"
#: src/components/systems-table/systems-table.tsx:166
msgid "Disk"
msgstr "磁盘"
#: src/components/routes/system.tsx:426
msgid "Disk I/O"
msgstr "磁盘I/O"
#: src/components/charts/disk-chart.tsx:74
#: src/components/routes/system.tsx:415
#: src/lib/utils.ts:301
msgid "Disk Usage"
msgstr "磁盘使用"
#: src/components/routes/system.tsx:495
msgid "Disk usage of {extraFsName}"
msgstr "{extraFsName}的磁盘使用"
#: src/components/routes/system.tsx:386
msgid "Docker CPU Usage"
msgstr "Docker CPU使用"
#: src/components/routes/system.tsx:407
msgid "Docker Memory Usage"
msgstr "Docker内存使用"
#: src/components/routes/system.tsx:452
msgid "Docker Network I/O"
msgstr "Docker网络I/O"
#: src/components/command-palette.tsx:125
msgid "Documentation"
msgstr "文档"
#: src/components/login/auth-form.tsx:158
msgid "email"
msgstr "电子邮件"
#: src/components/login/auth-form.tsx:152
#: src/components/login/forgot-pass-form.tsx:53
msgid "Email"
msgstr "电子邮件"
#: src/components/routes/settings/notifications.tsx:91
msgid "Email notifications"
msgstr "电子邮件通知"
#: src/components/login/login.tsx:36
msgid "Enter email address to reset password"
msgstr "输入电子邮件地址以重置密码"
#: src/components/routes/settings/notifications.tsx:111
msgid "Enter email address..."
msgstr "输入电子邮件地址..."
#: src/components/login/auth-form.tsx:256
#: src/components/routes/settings/config-yaml.tsx:28
#: src/components/routes/settings/notifications.tsx:187
msgid "Error"
msgstr "错误"
#: src/components/routes/home.tsx:81
msgid "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
msgstr "在过去的{2, plural, one {# 分钟} other {# 分钟}}中超过{0}{1}"
#: src/components/routes/settings/config-yaml.tsx:72
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
msgstr "未在<0>config.yml</0>中定义的现有系统将被删除。请定期备份。"
#: src/components/routes/settings/config-yaml.tsx:93
msgid "Export configuration"
msgstr "导出配置"
#: src/components/routes/settings/config-yaml.tsx:48
msgid "Export your current systems configuration."
msgstr "导出您当前的系统配置。"
#: src/lib/utils.ts:38
msgid "Failed to authenticate"
msgstr "认证失败"
#: src/components/routes/settings/layout.tsx:39
#: src/components/routes/settings/notifications.tsx:62
msgid "Failed to save settings"
msgstr "保存设置失败"
#: src/components/routes/settings/notifications.tsx:188
msgid "Failed to send test notification"
msgstr "发送测试通知失败"
#: src/components/alerts/alerts-system.tsx:27
msgid "Failed to update alert"
msgstr "更新警报失败"
#: src/components/routes/system.tsx:539
#: src/components/systems-table/systems-table.tsx:324
msgid "Filter..."
msgstr "过滤..."
#: src/components/alerts/alerts-system.tsx:225
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
msgstr "持续<0>{min}</0> {min, plural, one {分钟} other {分钟}}"
#: src/components/login/auth-form.tsx:337
msgid "Forgot password?"
msgstr "忘记密码?"
#. Context: General settings
#: src/components/routes/settings/general.tsx:33
#: src/components/routes/settings/layout.tsx:51
msgid "General"
msgstr "常规"
#: src/components/add-system.tsx:119
msgid "Host / IP"
msgstr "主机/IP"
#: src/components/login/forgot-pass-form.tsx:93
msgid "If you've lost the password to your admin account, you may reset it using the following command."
msgstr "如果您丢失了管理员账户的密码,可以使用以下命令重置。"
#: src/components/login/auth-form.tsx:16
msgid "Invalid email address."
msgstr "无效的电子邮件地址。"
#. Linux kernel
#: src/components/routes/system.tsx:254
msgid "Kernel"
msgstr "内核"
#: src/components/routes/settings/general.tsx:45
msgid "Language"
msgstr "语言"
#. Light theme
#: src/components/mode-toggle.tsx:16
msgid "Light"
msgstr "浅色"
#: src/components/navbar.tsx:113
msgid "Log Out"
msgstr "登出"
#: src/components/login/login.tsx:17
msgid "Login"
msgstr "登录"
#: src/components/login/auth-form.tsx:42
#: src/components/login/forgot-pass-form.tsx:15
msgid "Login attempt failed"
msgstr "登录尝试失败"
#: src/components/command-palette.tsx:157
#: src/components/navbar.tsx:86
msgid "Logs"
msgstr "日志"
#: src/components/routes/settings/notifications.tsx:80
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
msgstr "在寻找创建警报的位置吗?点击系统表中的铃铛<0/>图标。"
#: src/components/routes/settings/layout.tsx:85
msgid "Manage display and notification preferences."
msgstr "管理显示和通知偏好。"
#. Chart select field. Please try to keep this short.
#: src/components/routes/system.tsx:571
msgid "Max 1 min"
msgstr "最大1分钟"
#: src/components/systems-table/systems-table.tsx:159
msgid "Memory"
msgstr "内存"
#: src/components/routes/system.tsx:397
#: src/lib/utils.ts:295
msgid "Memory Usage"
msgstr "内存使用"
#: src/components/routes/system.tsx:408
msgid "Memory usage of docker containers"
msgstr "Docker容器的内存使用"
#: src/components/add-system.tsx:113
msgid "Name"
msgstr "名称"
#: src/components/systems-table/systems-table.tsx:173
msgid "Net"
msgstr "网络"
#: src/components/routes/system.tsx:453
msgid "Network traffic of docker containers"
msgstr "Docker容器的网络流量"
#: src/components/routes/system.tsx:438
msgid "Network traffic of public interfaces"
msgstr "公共接口的网络流量"
#: src/components/command-palette.tsx:50
msgid "No results found."
msgstr "未找到结果。"
#: src/components/systems-table/systems-table.tsx:400
msgid "No systems found."
msgstr "未找到系统。"
#: src/components/command-palette.tsx:111
#: src/components/routes/settings/layout.tsx:56
#: src/components/routes/settings/notifications.tsx:74
msgid "Notifications"
msgstr "通知"
#: src/components/login/auth-form.tsx:308
msgid "OAuth 2 / OIDC support"
msgstr "支持OAuth 2 / OIDC"
#: src/components/routes/settings/config-yaml.tsx:61
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
msgstr "每次重启时,数据库中的系统将更新以匹配文件中定义的系统。"
#: src/components/systems-table/systems-table.tsx:219
msgid "Open menu"
msgstr "打开菜单"
#: src/components/login/auth-form.tsx:227
msgid "Or continue with"
msgstr "或继续使用"
#: src/components/alerts/alert-button.tsx:109
msgid "Overwrite existing alerts"
msgstr "覆盖现有警报"
#: src/components/command-palette.tsx:85
msgid "Page"
msgstr "页面"
#: src/components/command-palette.tsx:72
msgid "Pages / Settings"
msgstr "页面/设置"
#: src/components/login/auth-form.tsx:171
#: src/components/login/auth-form.tsx:176
msgid "Password"
msgstr "密码"
#: src/components/login/auth-form.tsx:17
msgid "Password must be at least 10 characters."
msgstr "密码必须至少10个字符。"
#: src/components/login/forgot-pass-form.tsx:33
msgid "Password reset request received"
msgstr "已收到密码重置请求"
#: src/components/systems-table/systems-table.tsx:241
msgid "Pause"
msgstr "暂停"
#: src/components/routes/settings/notifications.tsx:95
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
msgstr "请<0>配置SMTP服务器</0>以确保警报被传递。"
#: src/components/alerts/alerts-system.tsx:28
msgid "Please check logs for more details."
msgstr "请检查日志以获取更多详细信息。"
#: src/components/login/auth-form.tsx:43
#: src/components/login/forgot-pass-form.tsx:16
msgid "Please check your credentials and try again"
msgstr "请检查您的凭据并重试"
#: src/components/login/login.tsx:34
msgid "Please create an admin account"
msgstr "请创建一个管理员账户"
#: src/components/login/auth-form.tsx:257
msgid "Please enable pop-ups for this site"
msgstr "请为此网站启用弹出窗口"
#: src/lib/utils.ts:39
msgid "Please log in again"
msgstr "请重新登录"
#: src/components/login/auth-form.tsx:316
msgid "Please see <0>the documentation</0> for instructions."
msgstr "请参阅<0>文档</0>以获取说明。"
#: src/components/login/login.tsx:38
msgid "Please sign in to your account"
msgstr "请登录您的账户"
#: src/components/add-system.tsx:125
msgid "Port"
msgstr "端口"
#: src/components/routes/system.tsx:398
msgid "Precise utilization at the recorded time"
msgstr "记录时间的精确使用率"
#: src/components/routes/settings/general.tsx:58
msgid "Preferred Language"
msgstr "首选语言"
#. Use 'Key' if your language requires many more characters
#: src/components/add-system.tsx:131
msgid "Public Key"
msgstr "公钥"
#. Context is disk read
#: src/components/charts/area-chart.tsx:56
#: src/components/charts/area-chart.tsx:66
msgid "Read"
msgstr "读取"
#. Context is network bytes received (download)
#: src/components/charts/area-chart.tsx:61
msgid "Received"
msgstr "接收"
#: src/components/login/forgot-pass-form.tsx:76
msgid "Reset Password"
msgstr "重置密码"
#: src/components/systems-table/systems-table.tsx:236
msgid "Resume"
msgstr "恢复"
#: src/components/routes/settings/notifications.tsx:117
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
msgstr "使用回车键或逗号保存地址。留空以禁用电子邮件通知。"
#: src/components/routes/settings/general.tsx:106
#: src/components/routes/settings/notifications.tsx:167
msgid "Save Settings"
msgstr "保存设置"
#: src/components/navbar.tsx:142
msgid "Search"
msgstr "搜索"
#: src/components/command-palette.tsx:47
msgid "Search for systems or settings..."
msgstr "搜索系统或设置..."
#: src/components/alerts/alert-button.tsx:71
msgid "See <0>notification settings</0> to configure how you receive alerts."
msgstr "查看<0>通知设置</0>以配置您接收警报的方式。"
#. Context is network bytes sent (upload)
#: src/components/charts/area-chart.tsx:60
msgid "Sent"
msgstr "发送"
#: src/components/routes/settings/general.tsx:100
msgid "Sets the default time range for charts when a system is viewed."
msgstr "设置查看系统时图表的默认时间范围。"
#: src/components/command-palette.tsx:96
#: src/components/command-palette.tsx:99
#: src/components/command-palette.tsx:114
#: src/components/routes/settings/layout.tsx:71
#: src/components/routes/settings/layout.tsx:82
msgid "Settings"
msgstr "设置"
#: src/components/routes/settings/layout.tsx:33
msgid "Settings saved"
msgstr "设置已保存"
#: src/components/login/auth-form.tsx:215
msgid "Sign in"
msgstr "登录"
#: src/components/command-palette.tsx:201
msgid "SMTP settings"
msgstr "SMTP设置"
#: src/lib/utils.ts:282
msgid "Status"
msgstr "状态"
#: src/components/routes/system.tsx:467
msgid "Swap space used by the system"
msgstr "系统使用的交换空间"
#: src/components/routes/system.tsx:466
msgid "Swap Usage"
msgstr "交换使用"
#. System theme
#: src/components/mode-toggle.tsx:26
#: src/components/systems-table/systems-table.tsx:110
#: src/components/systems-table/systems-table.tsx:121
msgid "System"
msgstr "系统"
#: src/components/navbar.tsx:78
msgid "Systems"
msgstr "系统"
#: src/components/routes/settings/config-yaml.tsx:55
msgid "Systems may be managed in a <0>config.yml</0> file inside your data directory."
msgstr "系统可以在数据目录中的<0>config.yml</0>文件中管理。"
#: src/components/routes/system.tsx:477
#: src/lib/utils.ts:314
msgid "Temperature"
msgstr "温度"
#: src/components/routes/system.tsx:478
msgid "Temperatures of system sensors"
msgstr "系统传感器的温度"
#: src/components/routes/settings/notifications.tsx:211
msgid "Test <0>URL</0>"
msgstr "测试<0>URL</0>"
#: src/components/routes/settings/notifications.tsx:182
msgid "Test notification sent"
msgstr "测试通知已发送"
#: src/components/add-system.tsx:104
msgid "The agent must be running on the system to connect. Copy the installation command for the agent below."
msgstr "代理必须在系统上运行才能连接。复制下面的代理安装命令。"
#: src/components/add-system.tsx:95
msgid "The agent must be running on the system to connect. Copy the<0>docker-compose.yml</0> for the agent below."
msgstr "代理必须在系统上运行才能连接。复制下面的<0>docker-compose.yml</0>。"
#: src/components/login/forgot-pass-form.tsx:98
msgid "Then log into the backend and reset your user account password in the users table."
msgstr "然后登录到后台并在用户表中重置您的用户账户密码。"
#: src/components/systems-table/systems-table.tsx:264
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
msgstr "此操作无法撤销。这将永久删除数据库中{name}的所有当前记录。"
#: src/components/routes/system.tsx:507
msgid "Throughput of {extraFsName}"
msgstr "{extraFsName}的吞吐量"
#: src/components/routes/system.tsx:427
msgid "Throughput of root filesystem"
msgstr "根文件系统的吞吐量"
#: src/components/routes/settings/notifications.tsx:106
msgid "To email(s)"
msgstr "发送到电子邮件"
#: src/components/routes/system.tsx:350
#: src/components/routes/system.tsx:363
msgid "Toggle grid"
msgstr "切换网格"
#: src/components/mode-toggle.tsx:33
msgid "Toggle theme"
msgstr "切换主题"
#: src/lib/utils.ts:317
msgid "Triggers when any sensor exceeds a threshold"
msgstr "当任何传感器超过阈值时触发"
#: src/lib/utils.ts:310
msgid "Triggers when combined up/down exceeds a threshold"
msgstr "当组合的上/下超过阈值时触发"
#: src/lib/utils.ts:292
msgid "Triggers when CPU usage exceeds a threshold"
msgstr "当CPU使用率超过阈值时触发"
#: src/lib/utils.ts:298
msgid "Triggers when memory usage exceeds a threshold"
msgstr "当内存使用率超过阈值时触发"
#: src/lib/utils.ts:285
msgid "Triggers when status switches between up and down"
msgstr "当状态在上和下之间切换时触发"
#: src/lib/utils.ts:304
msgid "Triggers when usage of any disk exceeds a threshold"
msgstr "当任何磁盘的使用超过阈值时触发"
#: src/components/systems-table/systems-table.tsx:320
msgid "Updated in real time. Click on a system to view information."
msgstr "实时更新。点击系统查看信息。"
#: src/components/routes/system.tsx:253
msgid "Uptime"
msgstr "正常运行时间"
#: src/components/routes/system.tsx:494
msgid "Usage"
msgstr "使用"
#: src/components/routes/system.tsx:415
msgid "Usage of root partition"
msgstr "根分区的使用"
#: src/components/charts/mem-chart.tsx:65
#: src/components/charts/swap-chart.tsx:56
msgid "Used"
msgstr "已用"
#: src/components/login/auth-form.tsx:138
msgid "username"
msgstr "用户名"
#: src/components/login/auth-form.tsx:131
msgid "Username"
msgstr "用户名"
#: src/components/command-palette.tsx:143
#: src/components/navbar.tsx:70
msgid "Users"
msgstr "用户"
#: src/components/routes/system.tsx:603
msgid "Waiting for enough records to display"
msgstr "等待足够的记录以显示"
#: src/components/routes/settings/general.tsx:48
msgid "Want to help us make our translations even better? Check out <0>Crowdin</0> for more details."
msgstr "想帮助我们改进翻译吗?查看<0>Crowdin</0>以获取更多详细信息。"
#: src/components/routes/settings/notifications.tsx:124
msgid "Webhook / Push notifications"
msgstr "Webhook / 推送通知"
#. Context is disk write
#: src/components/charts/area-chart.tsx:55
#: src/components/charts/area-chart.tsx:65
msgid "Write"
msgstr "写入"
#: src/components/routes/settings/layout.tsx:61
msgid "YAML Config"
msgstr "YAML配置"
#: src/components/routes/settings/config-yaml.tsx:45
msgid "YAML Configuration"
msgstr "YAML配置"
#: src/components/routes/settings/layout.tsx:34
msgid "Your user settings have been updated."
msgstr "您的用户设置已更新。"

View File

@@ -0,0 +1,808 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2024-11-01 11:30-0400\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: zh\n"
"Project-Id-Version: beszel\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-11-04 20:47\n"
"Last-Translator: \n"
"Language-Team: Chinese Traditional, Hong Kong\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Crowdin-Project: beszel\n"
"X-Crowdin-Project-ID: 733311\n"
"X-Crowdin-Language: zh-HK\n"
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 16\n"
#: src/components/routes/system.tsx:242
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# 天} other {# 天}}"
#: src/components/routes/system.tsx:240
msgid "{hours, plural, one {# hour} other {# hours}}"
msgstr "{hours, plural, one {# 小時} other {# 小時}}"
#: src/lib/utils.ts:139
msgid "1 hour"
msgstr "1小時"
#: src/lib/utils.ts:162
msgid "1 week"
msgstr "1週"
#: src/lib/utils.ts:147
msgid "12 hours"
msgstr "12小時"
#: src/lib/utils.ts:155
msgid "24 hours"
msgstr "24小時"
#: src/lib/utils.ts:170
msgid "30 days"
msgstr "30天"
#. Table column
#: src/components/systems-table/systems-table.tsx:207
msgid "Actions"
msgstr "操作"
#: src/components/routes/home.tsx:62
msgid "Active Alerts"
msgstr "活動警報"
#: src/components/add-system.tsx:74
msgid "Add <0>System</0>"
msgstr "添加<0>系統</0>"
#: src/components/add-system.tsx:83
msgid "Add New System"
msgstr "添加新系統"
#: src/components/add-system.tsx:167
#: src/components/add-system.tsx:178
msgid "Add system"
msgstr "添加系統"
#: src/components/routes/settings/notifications.tsx:156
msgid "Add URL"
msgstr "添加URL"
#: src/components/routes/settings/general.tsx:81
msgid "Adjust display options for charts."
msgstr "調整圖表的顯示選項。"
#: src/components/command-palette.tsx:133
#: src/components/command-palette.tsx:146
#: src/components/command-palette.tsx:160
#: src/components/command-palette.tsx:174
#: src/components/command-palette.tsx:189
#: src/components/command-palette.tsx:204
msgid "Admin"
msgstr "管理員"
#: src/components/systems-table/systems-table.tsx:186
msgid "Agent"
msgstr "代理"
#: src/components/alerts/alert-button.tsx:32
#: src/components/alerts/alert-button.tsx:68
msgid "Alerts"
msgstr "警報"
#: src/components/alerts/alert-button.tsx:88
#: src/components/systems-table/systems-table.tsx:317
msgid "All Systems"
msgstr "所有系統"
#: src/components/systems-table/systems-table.tsx:261
msgid "Are you sure you want to delete {name}?"
msgstr "您確定要刪除{name}嗎?"
#: src/components/command-palette.tsx:186
#: src/components/navbar.tsx:102
msgid "Auth Providers"
msgstr "認證提供者"
#: src/components/copy-to-clipboard.tsx:16
msgid "Automatic copy requires a secure context."
msgstr "自動複製需要安全的上下文。"
#: src/components/routes/system.tsx:568
msgid "Average"
msgstr "平均"
#: src/components/routes/system.tsx:387
msgid "Average CPU utilization of containers"
msgstr "容器的平均CPU使用率"
#: src/components/alerts/alerts-system.tsx:204
msgid "Average exceeds <0>{value}{0}</0>"
msgstr "平均值超過<0>{value}{0}</0>"
#: src/components/routes/system.tsx:376
msgid "Average system-wide CPU utilization"
msgstr "系統範圍內的平均CPU使用率"
#: src/components/command-palette.tsx:171
#: src/components/navbar.tsx:94
msgid "Backups"
msgstr "備份"
#: src/components/routes/system.tsx:436
#: src/lib/utils.ts:307
msgid "Bandwidth"
msgstr "帶寬"
#: src/components/login/auth-form.tsx:313
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
msgstr "Beszel支持OpenID Connect和許多OAuth2認證提供者。"
#: src/components/routes/settings/notifications.tsx:127
msgid "Beszel uses <0>Shoutrrr</0> to integrate with popular notification services."
msgstr "Beszel使用<0>Shoutrrr</0>與流行的通知服務集成。"
#: src/components/add-system.tsx:88
msgid "Binary"
msgstr "二進制"
#: src/components/charts/mem-chart.tsx:89
msgid "Cache / Buffers"
msgstr "緩存/緩衝區"
#: src/components/systems-table/systems-table.tsx:272
msgid "Cancel"
msgstr "取消"
#: src/components/routes/settings/config-yaml.tsx:68
msgid "Caution - potential data loss"
msgstr "注意 - 可能的數據丟失"
#: src/components/routes/settings/general.tsx:36
msgid "Change general application options."
msgstr "更改一般應用選項。"
#: src/components/routes/settings/general.tsx:78
msgid "Chart options"
msgstr "圖表選項"
#: src/components/login/forgot-pass-form.tsx:34
msgid "Check {email} for a reset link."
msgstr "檢查{email}以獲取重置鏈接。"
#: src/components/routes/settings/layout.tsx:40
msgid "Check logs for more details."
msgstr "檢查日誌以獲取更多詳細信息。"
#: src/components/routes/settings/notifications.tsx:183
msgid "Check your notification service"
msgstr "檢查您的通知服務"
#: src/components/add-system.tsx:153
msgid "Click to copy"
msgstr "點擊以複製"
#. Context: table columns
#: src/components/systems-table/systems-table.tsx:328
msgid "Columns"
msgstr "欄"
#: src/components/login/forgot-pass-form.tsx:83
#: src/components/login/forgot-pass-form.tsx:89
msgid "Command line instructions"
msgstr "命令行指令"
#: src/components/routes/settings/notifications.tsx:77
msgid "Configure how you receive alert notifications."
msgstr "配置您接收警報通知的方式。"
#: src/components/login/auth-form.tsx:189
#: src/components/login/auth-form.tsx:194
msgid "Confirm password"
msgstr "確認密碼"
#: src/components/systems-table/systems-table.tsx:278
msgid "Continue"
msgstr "繼續"
#: src/lib/utils.ts:25
msgid "Copied to clipboard"
msgstr "已複製到剪貼板"
#: src/components/add-system.tsx:164
msgid "Copy"
msgstr "複製"
#: src/components/systems-table/systems-table.tsx:247
msgid "Copy host"
msgstr "複製主機"
#: src/components/add-system.tsx:175
msgid "Copy Linux command"
msgstr "複製Linux命令"
#: src/components/copy-to-clipboard.tsx:13
msgid "Copy text"
msgstr "複製文本"
#: src/components/systems-table/systems-table.tsx:152
msgid "CPU"
msgstr "CPU"
#: src/components/charts/area-chart.tsx:52
#: src/components/routes/system.tsx:375
#: src/lib/utils.ts:289
msgid "CPU Usage"
msgstr "CPU使用率"
#: src/components/login/auth-form.tsx:215
msgid "Create account"
msgstr "創建帳戶"
#. Dark theme
#: src/components/mode-toggle.tsx:21
msgid "Dark"
msgstr "深色"
#: src/components/command-palette.tsx:82
#: src/components/routes/home.tsx:35
msgid "Dashboard"
msgstr "儀表板"
#: src/components/routes/settings/general.tsx:85
msgid "Default time period"
msgstr "默認時間段"
#: src/components/systems-table/systems-table.tsx:253
msgid "Delete"
msgstr "刪除"
#: src/components/systems-table/systems-table.tsx:166
msgid "Disk"
msgstr "磁碟"
#: src/components/routes/system.tsx:426
msgid "Disk I/O"
msgstr "磁碟I/O"
#: src/components/charts/disk-chart.tsx:74
#: src/components/routes/system.tsx:415
#: src/lib/utils.ts:301
msgid "Disk Usage"
msgstr "磁碟使用"
#: src/components/routes/system.tsx:495
msgid "Disk usage of {extraFsName}"
msgstr "{extraFsName}的磁碟使用"
#: src/components/routes/system.tsx:386
msgid "Docker CPU Usage"
msgstr "Docker CPU使用"
#: src/components/routes/system.tsx:407
msgid "Docker Memory Usage"
msgstr "Docker記憶體使用"
#: src/components/routes/system.tsx:452
msgid "Docker Network I/O"
msgstr "Docker網絡I/O"
#: src/components/command-palette.tsx:125
msgid "Documentation"
msgstr "文檔"
#: src/components/login/auth-form.tsx:158
msgid "email"
msgstr "電子郵件"
#: src/components/login/auth-form.tsx:152
#: src/components/login/forgot-pass-form.tsx:53
msgid "Email"
msgstr "電子郵件"
#: src/components/routes/settings/notifications.tsx:91
msgid "Email notifications"
msgstr "電子郵件通知"
#: src/components/login/login.tsx:36
msgid "Enter email address to reset password"
msgstr "輸入電子郵件地址以重置密碼"
#: src/components/routes/settings/notifications.tsx:111
msgid "Enter email address..."
msgstr "輸入電子郵件地址..."
#: src/components/login/auth-form.tsx:256
#: src/components/routes/settings/config-yaml.tsx:28
#: src/components/routes/settings/notifications.tsx:187
msgid "Error"
msgstr "錯誤"
#: src/components/routes/home.tsx:81
msgid "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
msgstr "在過去的{2, plural, one {# 分鐘} other {# 分鐘}}中超過{0}{1}"
#: src/components/routes/settings/config-yaml.tsx:72
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
msgstr "未在<0>config.yml</0>中定義的現有系統將被刪除。請定期備份。"
#: src/components/routes/settings/config-yaml.tsx:93
msgid "Export configuration"
msgstr "導出配置"
#: src/components/routes/settings/config-yaml.tsx:48
msgid "Export your current systems configuration."
msgstr "導出您當前的系統配置。"
#: src/lib/utils.ts:38
msgid "Failed to authenticate"
msgstr "認證失敗"
#: src/components/routes/settings/layout.tsx:39
#: src/components/routes/settings/notifications.tsx:62
msgid "Failed to save settings"
msgstr "保存設置失敗"
#: src/components/routes/settings/notifications.tsx:188
msgid "Failed to send test notification"
msgstr "發送測試通知失敗"
#: src/components/alerts/alerts-system.tsx:27
msgid "Failed to update alert"
msgstr "更新警報失敗"
#: src/components/routes/system.tsx:539
#: src/components/systems-table/systems-table.tsx:324
msgid "Filter..."
msgstr "篩選..."
#: src/components/alerts/alerts-system.tsx:225
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
msgstr "持續<0>{min}</0> {min, plural, one {分鐘} other {分鐘}}"
#: src/components/login/auth-form.tsx:337
msgid "Forgot password?"
msgstr "忘記密碼?"
#. Context: General settings
#: src/components/routes/settings/general.tsx:33
#: src/components/routes/settings/layout.tsx:51
msgid "General"
msgstr "一般"
#: src/components/add-system.tsx:119
msgid "Host / IP"
msgstr "主機 / IP"
#: src/components/login/forgot-pass-form.tsx:93
msgid "If you've lost the password to your admin account, you may reset it using the following command."
msgstr "如果您丟失了管理員帳戶的密碼,可以使用以下命令重置。"
#: src/components/login/auth-form.tsx:16
msgid "Invalid email address."
msgstr "無效的電子郵件地址。"
#. Linux kernel
#: src/components/routes/system.tsx:254
msgid "Kernel"
msgstr "內核"
#: src/components/routes/settings/general.tsx:45
msgid "Language"
msgstr "語言"
#. Light theme
#: src/components/mode-toggle.tsx:16
msgid "Light"
msgstr "淺色"
#: src/components/navbar.tsx:113
msgid "Log Out"
msgstr "登出"
#: src/components/login/login.tsx:17
msgid "Login"
msgstr "登錄"
#: src/components/login/auth-form.tsx:42
#: src/components/login/forgot-pass-form.tsx:15
msgid "Login attempt failed"
msgstr "登錄嘗試失敗"
#: src/components/command-palette.tsx:157
#: src/components/navbar.tsx:86
msgid "Logs"
msgstr "日誌"
#: src/components/routes/settings/notifications.tsx:80
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
msgstr "在尋找創建警報的位置嗎?點擊系統表中的鈴鐺<0/>圖標。"
#: src/components/routes/settings/layout.tsx:85
msgid "Manage display and notification preferences."
msgstr "管理顯示和通知偏好。"
#. Chart select field. Please try to keep this short.
#: src/components/routes/system.tsx:571
msgid "Max 1 min"
msgstr "最大1分鐘"
#: src/components/systems-table/systems-table.tsx:159
msgid "Memory"
msgstr "記憶體"
#: src/components/routes/system.tsx:397
#: src/lib/utils.ts:295
msgid "Memory Usage"
msgstr "記憶體使用"
#: src/components/routes/system.tsx:408
msgid "Memory usage of docker containers"
msgstr "Docker容器的記憶體使用"
#: src/components/add-system.tsx:113
msgid "Name"
msgstr "名稱"
#: src/components/systems-table/systems-table.tsx:173
msgid "Net"
msgstr "網絡"
#: src/components/routes/system.tsx:453
msgid "Network traffic of docker containers"
msgstr "Docker容器的網絡流量"
#: src/components/routes/system.tsx:438
msgid "Network traffic of public interfaces"
msgstr "公共接口的網絡流量"
#: src/components/command-palette.tsx:50
msgid "No results found."
msgstr "未找到結果。"
#: src/components/systems-table/systems-table.tsx:400
msgid "No systems found."
msgstr "未找到系統。"
#: src/components/command-palette.tsx:111
#: src/components/routes/settings/layout.tsx:56
#: src/components/routes/settings/notifications.tsx:74
msgid "Notifications"
msgstr "通知"
#: src/components/login/auth-form.tsx:308
msgid "OAuth 2 / OIDC support"
msgstr "支持OAuth 2 / OIDC"
#: src/components/routes/settings/config-yaml.tsx:61
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
msgstr "每次重啟時,數據庫中的系統將更新以匹配文件中定義的系統。"
#: src/components/systems-table/systems-table.tsx:219
msgid "Open menu"
msgstr "打開菜單"
#: src/components/login/auth-form.tsx:227
msgid "Or continue with"
msgstr "或繼續使用"
#: src/components/alerts/alert-button.tsx:109
msgid "Overwrite existing alerts"
msgstr "覆蓋現有警報"
#: src/components/command-palette.tsx:85
msgid "Page"
msgstr "頁面"
#: src/components/command-palette.tsx:72
msgid "Pages / Settings"
msgstr "頁面 / 設置"
#: src/components/login/auth-form.tsx:171
#: src/components/login/auth-form.tsx:176
msgid "Password"
msgstr "密碼"
#: src/components/login/auth-form.tsx:17
msgid "Password must be at least 10 characters."
msgstr "密碼必須至少10個字符。"
#: src/components/login/forgot-pass-form.tsx:33
msgid "Password reset request received"
msgstr "已收到密碼重置請求"
#: src/components/systems-table/systems-table.tsx:241
msgid "Pause"
msgstr "暫停"
#: src/components/routes/settings/notifications.tsx:95
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
msgstr "請<0>配置SMTP服務器</0>以確保警報被傳送。"
#: src/components/alerts/alerts-system.tsx:28
msgid "Please check logs for more details."
msgstr "請檢查日誌以獲取更多詳細信息。"
#: src/components/login/auth-form.tsx:43
#: src/components/login/forgot-pass-form.tsx:16
msgid "Please check your credentials and try again"
msgstr "請檢查您的憑證並重試"
#: src/components/login/login.tsx:34
msgid "Please create an admin account"
msgstr "請創建一個管理員帳戶"
#: src/components/login/auth-form.tsx:257
msgid "Please enable pop-ups for this site"
msgstr "請為此網站啟用彈出窗口"
#: src/lib/utils.ts:39
msgid "Please log in again"
msgstr "請重新登錄"
#: src/components/login/auth-form.tsx:316
msgid "Please see <0>the documentation</0> for instructions."
msgstr "請參閱<0>文檔</0>以獲取說明。"
#: src/components/login/login.tsx:38
msgid "Please sign in to your account"
msgstr "請登錄到您的帳戶"
#: src/components/add-system.tsx:125
msgid "Port"
msgstr "端口"
#: src/components/routes/system.tsx:398
msgid "Precise utilization at the recorded time"
msgstr "記錄時間的精確使用率"
#: src/components/routes/settings/general.tsx:58
msgid "Preferred Language"
msgstr "首選語言"
#. Use 'Key' if your language requires many more characters
#: src/components/add-system.tsx:131
msgid "Public Key"
msgstr "公鑰"
#. Context is disk read
#: src/components/charts/area-chart.tsx:56
#: src/components/charts/area-chart.tsx:66
msgid "Read"
msgstr "讀取"
#. Context is network bytes received (download)
#: src/components/charts/area-chart.tsx:61
msgid "Received"
msgstr "接收"
#: src/components/login/forgot-pass-form.tsx:76
msgid "Reset Password"
msgstr "重置密碼"
#: src/components/systems-table/systems-table.tsx:236
msgid "Resume"
msgstr "恢復"
#: src/components/routes/settings/notifications.tsx:117
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
msgstr "使用回車鍵或逗號保存地址。留空以禁用電子郵件通知。"
#: src/components/routes/settings/general.tsx:106
#: src/components/routes/settings/notifications.tsx:167
msgid "Save Settings"
msgstr "保存設置"
#: src/components/navbar.tsx:142
msgid "Search"
msgstr "搜索"
#: src/components/command-palette.tsx:47
msgid "Search for systems or settings..."
msgstr "搜索系統或設置..."
#: src/components/alerts/alert-button.tsx:71
msgid "See <0>notification settings</0> to configure how you receive alerts."
msgstr "查看<0>通知設置</0>以配置您接收警報的方式。"
#. Context is network bytes sent (upload)
#: src/components/charts/area-chart.tsx:60
msgid "Sent"
msgstr "發送"
#: src/components/routes/settings/general.tsx:100
msgid "Sets the default time range for charts when a system is viewed."
msgstr "設置查看系統時圖表的默認時間範圍。"
#: src/components/command-palette.tsx:96
#: src/components/command-palette.tsx:99
#: src/components/command-palette.tsx:114
#: src/components/routes/settings/layout.tsx:71
#: src/components/routes/settings/layout.tsx:82
msgid "Settings"
msgstr "設置"
#: src/components/routes/settings/layout.tsx:33
msgid "Settings saved"
msgstr "設置已保存"
#: src/components/login/auth-form.tsx:215
msgid "Sign in"
msgstr "登錄"
#: src/components/command-palette.tsx:201
msgid "SMTP settings"
msgstr "SMTP設置"
#: src/lib/utils.ts:282
msgid "Status"
msgstr "狀態"
#: src/components/routes/system.tsx:467
msgid "Swap space used by the system"
msgstr "系統使用的交換空間"
#: src/components/routes/system.tsx:466
msgid "Swap Usage"
msgstr "交換使用"
#. System theme
#: src/components/mode-toggle.tsx:26
#: src/components/systems-table/systems-table.tsx:110
#: src/components/systems-table/systems-table.tsx:121
msgid "System"
msgstr "系統"
#: src/components/navbar.tsx:78
msgid "Systems"
msgstr "系統"
#: src/components/routes/settings/config-yaml.tsx:55
msgid "Systems may be managed in a <0>config.yml</0> file inside your data directory."
msgstr "系統可以在您的數據目錄中的<0>config.yml</0>文件中管理。"
#: src/components/routes/system.tsx:477
#: src/lib/utils.ts:314
msgid "Temperature"
msgstr "溫度"
#: src/components/routes/system.tsx:478
msgid "Temperatures of system sensors"
msgstr "系統傳感器的溫度"
#: src/components/routes/settings/notifications.tsx:211
msgid "Test <0>URL</0>"
msgstr "測試<0>URL</0>"
#: src/components/routes/settings/notifications.tsx:182
msgid "Test notification sent"
msgstr "測試通知已發送"
#: src/components/add-system.tsx:104
msgid "The agent must be running on the system to connect. Copy the installation command for the agent below."
msgstr "代理必須在系統上運行才能連接。複製下面的代理安裝命令。"
#: src/components/add-system.tsx:95
msgid "The agent must be running on the system to connect. Copy the<0>docker-compose.yml</0> for the agent below."
msgstr "代理必須在系統上運行才能連接。複製下面的<0>docker-compose.yml</0>。"
#: src/components/login/forgot-pass-form.tsx:98
msgid "Then log into the backend and reset your user account password in the users table."
msgstr "然後登錄到後端並在用戶表中重置您的用戶帳戶密碼。"
#: src/components/systems-table/systems-table.tsx:264
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
msgstr "此操作無法撤銷。這將永久刪除數據庫中{name}的所有當前記錄。"
#: src/components/routes/system.tsx:507
msgid "Throughput of {extraFsName}"
msgstr "{extraFsName}的吞吐量"
#: src/components/routes/system.tsx:427
msgid "Throughput of root filesystem"
msgstr "根文件系統的吞吐量"
#: src/components/routes/settings/notifications.tsx:106
msgid "To email(s)"
msgstr "發送到電子郵件"
#: src/components/routes/system.tsx:350
#: src/components/routes/system.tsx:363
msgid "Toggle grid"
msgstr "切換網格"
#: src/components/mode-toggle.tsx:33
msgid "Toggle theme"
msgstr "切換主題"
#: src/lib/utils.ts:317
msgid "Triggers when any sensor exceeds a threshold"
msgstr "當任何傳感器超過閾值時觸發"
#: src/lib/utils.ts:310
msgid "Triggers when combined up/down exceeds a threshold"
msgstr "當組合的上/下超過閾值時觸發"
#: src/lib/utils.ts:292
msgid "Triggers when CPU usage exceeds a threshold"
msgstr "當CPU使用率超過閾值時觸發"
#: src/lib/utils.ts:298
msgid "Triggers when memory usage exceeds a threshold"
msgstr "當記憶體使用率超過閾值時觸發"
#: src/lib/utils.ts:285
msgid "Triggers when status switches between up and down"
msgstr "當狀態在上和下之間切換時觸發"
#: src/lib/utils.ts:304
msgid "Triggers when usage of any disk exceeds a threshold"
msgstr "當任何磁碟的使用超過閾值時觸發"
#: src/components/systems-table/systems-table.tsx:320
msgid "Updated in real time. Click on a system to view information."
msgstr "實時更新。點擊系統查看信息。"
#: src/components/routes/system.tsx:253
msgid "Uptime"
msgstr "正常運行時間"
#: src/components/routes/system.tsx:494
msgid "Usage"
msgstr "使用"
#: src/components/routes/system.tsx:415
msgid "Usage of root partition"
msgstr "根分區的使用"
#: src/components/charts/mem-chart.tsx:65
#: src/components/charts/swap-chart.tsx:56
msgid "Used"
msgstr "已用"
#: src/components/login/auth-form.tsx:138
msgid "username"
msgstr "用戶名"
#: src/components/login/auth-form.tsx:131
msgid "Username"
msgstr "用戶名"
#: src/components/command-palette.tsx:143
#: src/components/navbar.tsx:70
msgid "Users"
msgstr "用戶"
#: src/components/routes/system.tsx:603
msgid "Waiting for enough records to display"
msgstr "等待足夠的記錄以顯示"
#: src/components/routes/settings/general.tsx:48
msgid "Want to help us make our translations even better? Check out <0>Crowdin</0> for more details."
msgstr "想幫助我們改進翻譯嗎?查看<0>Crowdin</0>以獲取更多詳細信息。"
#: src/components/routes/settings/notifications.tsx:124
msgid "Webhook / Push notifications"
msgstr "Webhook / 推送通知"
#. Context is disk write
#: src/components/charts/area-chart.tsx:55
#: src/components/charts/area-chart.tsx:65
msgid "Write"
msgstr "寫入"
#: src/components/routes/settings/layout.tsx:61
msgid "YAML Config"
msgstr "YAML配置"
#: src/components/routes/settings/config-yaml.tsx:45
msgid "YAML Configuration"
msgstr "YAML配置"
#: src/components/routes/settings/layout.tsx:34
msgid "Your user settings have been updated."
msgstr "您的用戶設置已更新。"

View File

@@ -1,58 +1,24 @@
import './index.css'
import { Suspense, lazy, useEffect, StrictMode } from 'react'
import ReactDOM from 'react-dom/client'
import Home from './components/routes/home.tsx'
import { ThemeProvider } from './components/theme-provider.tsx'
import {
$authenticated,
$systems,
pb,
$publicKey,
$hubVersion,
$copyContent,
} from './lib/stores.ts'
import { ModeToggle } from './components/mode-toggle.tsx'
import {
cn,
updateUserSettings,
isAdmin,
isReadOnlyUser,
updateAlerts,
updateFavicon,
updateSystemList,
} from './lib/utils.ts'
import { buttonVariants } from './components/ui/button.tsx'
import {
DatabaseBackupIcon,
LockKeyholeIcon,
LogOutIcon,
LogsIcon,
ServerIcon,
SettingsIcon,
UserIcon,
UsersIcon,
} from 'lucide-react'
import { useStore } from '@nanostores/react'
import { Toaster } from './components/ui/toaster.tsx'
import { Logo } from './components/logo.tsx'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
DropdownMenuLabel,
} from './components/ui/dropdown-menu.tsx'
import { $router, Link } from './components/router.tsx'
import SystemDetail from './components/routes/system.tsx'
import { AddSystemButton } from './components/add-system.tsx'
import "./index.css"
// import { Suspense, lazy, useEffect, StrictMode } from "react"
import { Suspense, lazy, useEffect } from "react"
import ReactDOM from "react-dom/client"
import Home from "./components/routes/home.tsx"
import { ThemeProvider } from "./components/theme-provider.tsx"
import { DirectionProvider } from "@radix-ui/react-direction"
import { $authenticated, $systems, pb, $publicKey, $hubVersion, $copyContent, $direction } from "./lib/stores.ts"
import { updateUserSettings, updateAlerts, updateFavicon, updateSystemList } from "./lib/utils.ts"
import { useStore } from "@nanostores/react"
import { Toaster } from "./components/ui/toaster.tsx"
import { $router } from "./components/router.tsx"
import SystemDetail from "./components/routes/system.tsx"
import Navbar from "./components/navbar.tsx"
import { I18nProvider } from "@lingui/react"
import { i18n } from "@lingui/core"
// const ServerDetail = lazy(() => import('./components/routes/system.tsx'))
const CommandPalette = lazy(() => import('./components/command-palette.tsx'))
const LoginPage = lazy(() => import('./components/login/login.tsx'))
const CopyToClipboardDialog = lazy(() => import('./components/copy-to-clipboard.tsx'))
const Settings = lazy(() => import('./components/routes/settings/layout.tsx'))
const LoginPage = lazy(() => import("./components/login/login.tsx"))
const CopyToClipboardDialog = lazy(() => import("./components/copy-to-clipboard.tsx"))
const Settings = lazy(() => import("./components/routes/settings/layout.tsx"))
const App = () => {
const page = useStore($router)
@@ -65,7 +31,7 @@ const App = () => {
$authenticated.set(pb.authStore.isValid)
})
// get version / public key
pb.send('/api/beszel/getkey', {}).then((data) => {
pb.send("/api/beszel/getkey", {}).then((data) => {
$publicKey.set(data.key)
$hubVersion.set(data.v)
})
@@ -74,34 +40,34 @@ const App = () => {
// get alerts after system list is loaded
updateSystemList().then(updateAlerts)
return () => updateFavicon('favicon.svg')
return () => updateFavicon("favicon.svg")
}, [])
// update favicon
useEffect(() => {
if (!systems.length || !authenticated) {
updateFavicon('favicon.svg')
updateFavicon("favicon.svg")
} else {
let up = false
for (const system of systems) {
if (system.status === 'down') {
updateFavicon('favicon-red.svg')
if (system.status === "down") {
updateFavicon("favicon-red.svg")
return
} else if (system.status === 'up') {
} else if (system.status === "up") {
up = true
}
}
updateFavicon(up ? 'favicon-green.svg' : 'favicon.svg')
updateFavicon(up ? "favicon-green.svg" : "favicon.svg")
}
}, [systems])
if (!page) {
return <h1 className="text-3xl text-center my-14">404</h1>
} else if (page.path === '/') {
} else if (page.path === "/") {
return <Home />
} else if (page.route === 'server') {
} else if (page.route === "server") {
return <SystemDetail name={page.params.name} />
} else if (page.route === 'settings') {
} else if (page.route === "settings") {
return (
<Suspense>
<Settings />
@@ -113,113 +79,46 @@ const App = () => {
const Layout = () => {
const authenticated = useStore($authenticated)
const copyContent = useStore($copyContent)
const direction = useStore($direction)
if (!authenticated) {
return (
<Suspense>
<LoginPage />
</Suspense>
)
}
useEffect(() => {
document.documentElement.dir = direction
}, [direction])
return (
<>
<div className="container">
<div className="flex items-center h-14 md:h-16 bg-card px-4 pr-3 sm:px-6 border bt-0 rounded-md my-4">
<Link href="/" aria-label="Home" className={'p-2 pl-0'}>
<Logo className="h-[1.15em] fill-foreground" />
</Link>
<div className={'flex ml-auto items-center'}>
<ModeToggle />
<Link
href="/settings/general"
aria-label="Settings"
className={cn('', buttonVariants({ variant: 'ghost', size: 'icon' }))}
>
<SettingsIcon className="h-[1.2rem] w-[1.2rem]" />
</Link>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
aria-label="User Actions"
className={cn('', buttonVariants({ variant: 'ghost', size: 'icon' }))}
>
<UserIcon className="h-[1.2rem] w-[1.2rem]" />
</button>
</DropdownMenuTrigger>
<DropdownMenuContent align={isReadOnlyUser() ? 'end' : 'center'} className="min-w-44">
<DropdownMenuLabel>{pb.authStore.model?.email}</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
{isAdmin() && (
<>
<DropdownMenuItem asChild>
<a href="/_/" target="_blank">
<UsersIcon className="mr-2.5 h-4 w-4" />
<span>Users</span>
</a>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<a href="/_/#/collections?collectionId=2hz5ncl8tizk5nx" target="_blank">
<ServerIcon className="mr-2.5 h-4 w-4" />
<span>Systems</span>
</a>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<a href="/_/#/logs" target="_blank">
<LogsIcon className="mr-2.5 h-4 w-4" />
<span>Logs</span>
</a>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<a href="/_/#/settings/backups" target="_blank">
<DatabaseBackupIcon className="mr-2.5 h-4 w-4" />
<span>Backups</span>
</a>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<a href="/_/#/settings/auth-providers" target="_blank">
<LockKeyholeIcon className="mr-2.5 h-4 w-4" />
<span>Auth providers</span>
</a>
</DropdownMenuItem>
<DropdownMenuSeparator />
</>
)}
</DropdownMenuGroup>
<DropdownMenuItem onSelect={() => pb.authStore.clear()}>
<LogOutIcon className="mr-2.5 h-4 w-4" />
<span>Log out</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<AddSystemButton className="ml-2" />
</div>
</div>
</div>
<div className="container mb-14 relative">
<App />
<DirectionProvider dir={direction}>
{!authenticated ? (
<Suspense>
<CommandPalette />
<LoginPage />
</Suspense>
{copyContent && (
<Suspense>
<CopyToClipboardDialog content={copyContent} />
</Suspense>
)}
</div>
</>
) : (
<>
<div className="container">
<Navbar />
</div>
<div className="container mb-14 relative">
<App />
{copyContent && (
<Suspense>
<CopyToClipboardDialog content={copyContent} />
</Suspense>
)}
</div>
</>
)}
</DirectionProvider>
)
}
ReactDOM.createRoot(document.getElementById('app')!).render(
ReactDOM.createRoot(document.getElementById("app")!).render(
// strict mode in dev mounts / unmounts components twice
// and breaks the clipboard dialog
//<StrictMode>
<ThemeProvider>
<Layout />
<Toaster />
</ThemeProvider>
<I18nProvider i18n={i18n}>
<ThemeProvider>
<Layout />
<Toaster />
</ThemeProvider>
</I18nProvider>
//</StrictMode>
)

View File

@@ -1,9 +1,9 @@
import { RecordModel } from 'pocketbase'
import { RecordModel } from "pocketbase"
export interface SystemRecord extends RecordModel {
name: string
host: string
status: 'up' | 'down' | 'paused' | 'pending'
status: "up" | "down" | "paused" | "pending"
port: string
info: SystemInfo
v: string
@@ -132,13 +132,13 @@ export interface AlertRecord extends RecordModel {
// user: string
}
export type ChartTimes = '1h' | '12h' | '24h' | '1w' | '30d'
export type ChartTimes = "1h" | "12h" | "24h" | "1w" | "30d"
export interface ChartTimeData {
[key: string]: {
type: '1m' | '10m' | '20m' | '120m' | '480m'
type: "1m" | "10m" | "20m" | "120m" | "480m"
expectedInterval: number
label: string
label: () => string
ticks?: number
format: (timestamp: string) => string
getOffset: (endTime: Date) => Date
@@ -155,13 +155,23 @@ export type UserSettings = {
type ChartDataContainer = {
created: number | null
} & {
[key: string]: key extends 'created' ? never : ContainerStats
[key: string]: key extends "created" ? never : ContainerStats
}
export interface ChartData {
systemStats: SystemStatsRecord[]
containerData: ChartDataContainer[]
orientation: "right" | "left"
ticks: number[]
domain: number[]
chartTime: ChartTimes
}
interface AlertInfo {
name: () => string
unit: string
icon: any
desc: () => string
single?: boolean
max?: number
}

View File

@@ -1,103 +1,100 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: ['class'],
content: [
'./pages/**/*.{ts,tsx}',
'./components/**/*.{ts,tsx}',
'./app/**/*.{ts,tsx}',
'./src/**/*.{ts,tsx}',
],
prefix: '',
darkMode: ["class"],
content: ["./pages/**/*.{ts,tsx}", "./components/**/*.{ts,tsx}", "./app/**/*.{ts,tsx}", "./src/**/*.{ts,tsx}"],
prefix: "",
theme: {
container: {
center: true,
padding: '1rem',
padding: "1rem",
screens: {
'2xl': '1400px',
"2xl": "1400px",
},
},
extend: {
fontFamily: {
sans: 'Inter, sans-serif',
sans: "Inter, sans-serif",
// body: ['Inter', 'sans-serif'],
// display: ['Inter', 'sans-serif'],
},
screens: {
xs: '425px',
xs: "425px",
450: "450px",
},
colors: {
green: {
50: '#EBF9F0',
100: '#D8F3E1',
200: '#ADE6C0',
300: '#85DBA2',
400: '#5ACE81',
500: '#38BB63',
600: '#2D954F',
700: '#22723D',
800: '#164B28',
900: '#0C2715',
950: '#06140A',
50: "#EBF9F0",
100: "#D8F3E1",
200: "#ADE6C0",
300: "#85DBA2",
400: "#5ACE81",
500: "#38BB63",
600: "#2D954F",
700: "#22723D",
800: "#164B28",
900: "#0C2715",
950: "#06140A",
},
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))',
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: 'hsl(var(--secondary))',
foreground: 'hsl(var(--secondary-foreground))',
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: 'hsl(var(--destructive))',
foreground: 'hsl(var(--destructive-foreground))',
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: 'hsl(var(--muted))',
foreground: 'hsl(var(--muted-foreground))',
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: 'hsl(var(--accent))',
foreground: 'hsl(var(--accent-foreground))',
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: 'hsl(var(--popover))',
foreground: 'hsl(var(--popover-foreground))',
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))',
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
},
borderRadius: {
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)',
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
keyframes: {
'accordion-down': {
from: { height: '0' },
to: { height: 'var(--radix-accordion-content-height)' },
"accordion-down": {
from: { height: "0" },
to: { height: "var(--radix-accordion-content-height)" },
},
'accordion-up': {
from: { height: 'var(--radix-accordion-content-height)' },
to: { height: '0' },
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: "0" },
},
},
animation: {
'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out',
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
},
},
},
plugins: [
require('tailwindcss-animate'),
require("tailwindcss-animate"),
require("tailwindcss-rtl"),
function ({ addVariant }) {
addVariant('light', '.light &')
addVariant("light", ".light &")
},
],
}

Some files were not shown because too many files have changed in this diff Show More