mirror of
https://github.com/fankes/beszel.git
synced 2025-10-20 02:09:28 +08:00
rtl layout progress and updates to arabic translations
This commit is contained in:
@@ -84,18 +84,22 @@ func (a *Agent) Run(pubKey []byte, addr string) {
|
|||||||
|
|
||||||
func (a *Agent) gatherStats() system.CombinedData {
|
func (a *Agent) gatherStats() system.CombinedData {
|
||||||
slog.Debug("Getting stats")
|
slog.Debug("Getting stats")
|
||||||
systemData := system.CombinedData{
|
// systemData := system.CombinedData{
|
||||||
Stats: a.getSystemStats(),
|
// Stats: a.getSystemStats(),
|
||||||
Info: a.systemInfo,
|
// Info: a.systemInfo,
|
||||||
}
|
// }
|
||||||
slog.Debug("System stats", "data", systemData)
|
systemData := system.CombinedData{}
|
||||||
// add docker stats
|
// add docker stats (testing doing this first for docker 24)
|
||||||
if containerStats, err := a.dockerManager.getDockerStats(); err == nil {
|
if containerStats, err := a.dockerManager.getDockerStats(); err == nil {
|
||||||
systemData.Containers = containerStats
|
systemData.Containers = containerStats
|
||||||
slog.Debug("Docker stats", "data", systemData.Containers)
|
slog.Debug("Docker stats", "data", systemData.Containers)
|
||||||
} else {
|
} else {
|
||||||
slog.Debug("Error getting docker stats", "err", err)
|
slog.Debug("Error getting docker stats", "err", err)
|
||||||
}
|
}
|
||||||
|
systemData.Stats = a.getSystemStats()
|
||||||
|
systemData.Info = a.systemInfo
|
||||||
|
slog.Debug("System stats", "data", systemData)
|
||||||
|
|
||||||
// add extra filesystems
|
// add extra filesystems
|
||||||
systemData.Stats.ExtraFs = make(map[string]*system.FsStats)
|
systemData.Stats.ExtraFs = make(map[string]*system.FsStats)
|
||||||
for name, stats := range a.fsStats {
|
for name, stats := range a.fsStats {
|
||||||
|
@@ -25,18 +25,23 @@ type dockerManager struct {
|
|||||||
apiContainerList *[]container.ApiInfo // List of containers from Docker API
|
apiContainerList *[]container.ApiInfo // List of containers from Docker API
|
||||||
containerStatsMap map[string]*container.Stats // Keeps track of container stats
|
containerStatsMap map[string]*container.Stats // Keeps track of container stats
|
||||||
validIds map[string]struct{} // Map of valid container ids, used to prune invalid containers from containerStatsMap
|
validIds map[string]struct{} // Map of valid container ids, used to prune invalid containers from containerStatsMap
|
||||||
|
goodDockerVersion bool // Whether docker version is at least 25.0.0 (one-shot works correctly)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add goroutine to the queue
|
// Add goroutine to the queue
|
||||||
func (d *dockerManager) queue() {
|
func (d *dockerManager) queue() {
|
||||||
d.sem <- struct{}{}
|
|
||||||
d.wg.Add(1)
|
d.wg.Add(1)
|
||||||
|
if d.goodDockerVersion {
|
||||||
|
d.sem <- struct{}{}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove goroutine from the queue
|
// Remove goroutine from the queue
|
||||||
func (d *dockerManager) dequeue() {
|
func (d *dockerManager) dequeue() {
|
||||||
<-d.sem
|
|
||||||
d.wg.Done()
|
d.wg.Done()
|
||||||
|
if d.goodDockerVersion {
|
||||||
|
<-d.sem
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns stats for all running containers
|
// Returns stats for all running containers
|
||||||
@@ -47,6 +52,11 @@ func (dm *dockerManager) getDockerStats() ([]*container.Stats, error) {
|
|||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// test sleeping for 1 second if docker 24
|
||||||
|
if !dm.goodDockerVersion {
|
||||||
|
time.Sleep(time.Millisecond * 1100)
|
||||||
|
}
|
||||||
|
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&dm.apiContainerList); err != nil {
|
if err := json.NewDecoder(resp.Body).Decode(&dm.apiContainerList); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -89,11 +99,11 @@ func (dm *dockerManager) getDockerStats() ([]*container.Stats, error) {
|
|||||||
// retry failed containers separately so we can run them in parallel (docker 24 bug)
|
// retry failed containers separately so we can run them in parallel (docker 24 bug)
|
||||||
if len(failedContainters) > 0 {
|
if len(failedContainters) > 0 {
|
||||||
slog.Debug("Retrying failed containers", "count", len(failedContainters))
|
slog.Debug("Retrying failed containers", "count", len(failedContainters))
|
||||||
// time.Sleep(time.Millisecond * 1100)
|
time.Sleep(time.Millisecond * 1100) // this is a test for docker 24 bug
|
||||||
for _, ctr := range failedContainters {
|
for _, ctr := range failedContainters {
|
||||||
dm.wg.Add(1)
|
dm.queue()
|
||||||
go func() {
|
go func() {
|
||||||
defer dm.wg.Done()
|
defer dm.dequeue()
|
||||||
err = dm.updateContainerStats(ctr)
|
err = dm.updateContainerStats(ctr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("Error getting container stats", "err", err)
|
slog.Error("Error getting container stats", "err", err)
|
||||||
@@ -251,12 +261,9 @@ func newDockerManager() *dockerManager {
|
|||||||
Transport: transport,
|
Transport: transport,
|
||||||
},
|
},
|
||||||
containerStatsMap: make(map[string]*container.Stats),
|
containerStatsMap: make(map[string]*container.Stats),
|
||||||
|
sem: make(chan struct{}, 5),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure sem is initialized
|
|
||||||
concurrency := 200
|
|
||||||
defer func() { dockerClient.sem = make(chan struct{}, concurrency) }()
|
|
||||||
|
|
||||||
// Check docker version
|
// Check docker version
|
||||||
// (versions before 25.0.0 have a bug with one-shot which requires all requests to be made in one batch)
|
// (versions before 25.0.0 have a bug with one-shot which requires all requests to be made in one batch)
|
||||||
var versionInfo struct {
|
var versionInfo struct {
|
||||||
@@ -273,9 +280,10 @@ func newDockerManager() *dockerManager {
|
|||||||
|
|
||||||
// if version > 24, one-shot works correctly and we can limit concurrent operations
|
// if version > 24, one-shot works correctly and we can limit concurrent operations
|
||||||
if dockerVersion, err := semver.Parse(versionInfo.Version); err == nil && dockerVersion.Major > 24 {
|
if dockerVersion, err := semver.Parse(versionInfo.Version); err == nil && dockerVersion.Major > 24 {
|
||||||
concurrency = 5
|
dockerClient.goodDockerVersion = true
|
||||||
|
} else {
|
||||||
|
slog.Info(fmt.Sprintf("Docker %s is outdated. Upgrade if possible. See https://github.com/henrygd/beszel/issues/58", versionInfo.Version))
|
||||||
}
|
}
|
||||||
slog.Debug("Docker", "version", versionInfo.Version, "concurrency", concurrency)
|
|
||||||
|
|
||||||
return dockerClient
|
return dockerClient
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
@@ -1,5 +1,5 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en" dir="ltr">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg" />
|
||||||
|
19
beszel/site/package-lock.json
generated
19
beszel/site/package-lock.json
generated
@@ -14,6 +14,7 @@
|
|||||||
"@radix-ui/react-alert-dialog": "^1.1.2",
|
"@radix-ui/react-alert-dialog": "^1.1.2",
|
||||||
"@radix-ui/react-checkbox": "^1.1.2",
|
"@radix-ui/react-checkbox": "^1.1.2",
|
||||||
"@radix-ui/react-dialog": "^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-dropdown-menu": "^2.1.2",
|
||||||
"@radix-ui/react-label": "^2.1.0",
|
"@radix-ui/react-label": "^2.1.0",
|
||||||
"@radix-ui/react-select": "^2.1.2",
|
"@radix-ui/react-select": "^2.1.2",
|
||||||
@@ -31,7 +32,6 @@
|
|||||||
"cmdk": "^1.0.0",
|
"cmdk": "^1.0.0",
|
||||||
"d3-time": "^3.1.0",
|
"d3-time": "^3.1.0",
|
||||||
"i18next": "^23.16.4",
|
"i18next": "^23.16.4",
|
||||||
"i18next-browser-languagedetector": "^8.0.0",
|
|
||||||
"lucide-react": "^0.452.0",
|
"lucide-react": "^0.452.0",
|
||||||
"nanostores": "^0.11.3",
|
"nanostores": "^0.11.3",
|
||||||
"pocketbase": "^0.21.5",
|
"pocketbase": "^0.21.5",
|
||||||
@@ -50,6 +50,7 @@
|
|||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"postcss": "^8.4.47",
|
"postcss": "^8.4.47",
|
||||||
"tailwindcss": "^3.4.14",
|
"tailwindcss": "^3.4.14",
|
||||||
|
"tailwindcss-rtl": "^0.9.0",
|
||||||
"typescript": "^5.6.3",
|
"typescript": "^5.6.3",
|
||||||
"vite": "^5.4.9"
|
"vite": "^5.4.9"
|
||||||
},
|
},
|
||||||
@@ -4153,15 +4154,6 @@
|
|||||||
"@babel/runtime": "^7.23.2"
|
"@babel/runtime": "^7.23.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/i18next-browser-languagedetector": {
|
|
||||||
"version": "8.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.0.0.tgz",
|
|
||||||
"integrity": "sha512-zhXdJXTTCoG39QsrOCiOabnWj2jecouOqbchu3EfhtSHxIB5Uugnm9JaizenOy39h7ne3+fLikIjeW88+rgszw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.23.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/internmap": {
|
"node_modules/internmap": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
|
||||||
@@ -5344,6 +5336,13 @@
|
|||||||
"tailwindcss": ">=3.0.0 || insiders"
|
"tailwindcss": ">=3.0.0 || insiders"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tailwindcss-rtl": {
|
||||||
|
"version": "0.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tailwindcss-rtl/-/tailwindcss-rtl-0.9.0.tgz",
|
||||||
|
"integrity": "sha512-y7yC8QXjluDBEFMSX33tV6xMYrf0B3sa+tOB5JSQb6/G6laBU313a+Z+qxu55M1Qyn8tDMttjomsA8IsJD+k+w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/tailwindcss/node_modules/picocolors": {
|
"node_modules/tailwindcss/node_modules/picocolors": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
"@radix-ui/react-alert-dialog": "^1.1.2",
|
"@radix-ui/react-alert-dialog": "^1.1.2",
|
||||||
"@radix-ui/react-checkbox": "^1.1.2",
|
"@radix-ui/react-checkbox": "^1.1.2",
|
||||||
"@radix-ui/react-dialog": "^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-dropdown-menu": "^2.1.2",
|
||||||
"@radix-ui/react-label": "^2.1.0",
|
"@radix-ui/react-label": "^2.1.0",
|
||||||
"@radix-ui/react-select": "^2.1.2",
|
"@radix-ui/react-select": "^2.1.2",
|
||||||
@@ -50,6 +51,7 @@
|
|||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"postcss": "^8.4.47",
|
"postcss": "^8.4.47",
|
||||||
"tailwindcss": "^3.4.14",
|
"tailwindcss": "^3.4.14",
|
||||||
|
"tailwindcss-rtl": "^0.9.0",
|
||||||
"typescript": "^5.6.3",
|
"typescript": "^5.6.3",
|
||||||
"vite": "^5.4.9"
|
"vite": "^5.4.9"
|
||||||
},
|
},
|
||||||
|
@@ -41,8 +41,7 @@ export function AddSystemButton({ className }: { className?: string }) {
|
|||||||
# - /mnt/disk1/.beszel:/extra-filesystems/disk1:ro
|
# - /mnt/disk1/.beszel:/extra-filesystems/disk1:ro
|
||||||
environment:
|
environment:
|
||||||
PORT: ${port}
|
PORT: ${port}
|
||||||
KEY: "${publicKey}"
|
KEY: "${publicKey}"`)
|
||||||
# FILESYSTEM: /dev/sda1 # override the root partition / device for disk I/O stats`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function copyInstallCommand(port: string) {
|
function copyInstallCommand(port: string) {
|
||||||
@@ -73,7 +72,7 @@ export function AddSystemButton({ className }: { className?: string }) {
|
|||||||
variant="outline"
|
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" />
|
<PlusIcon className="h-4 w-4 -ms-1" />
|
||||||
{t("add")}
|
{t("add")}
|
||||||
<span className="hidden sm:inline">{t("system")}</span>
|
<span className="hidden sm:inline">{t("system")}</span>
|
||||||
</Button>
|
</Button>
|
||||||
@@ -104,31 +103,31 @@ export function AddSystemButton({ className }: { className?: string }) {
|
|||||||
<form onSubmit={handleSubmit as any}>
|
<form onSubmit={handleSubmit as any}>
|
||||||
<div className="grid gap-3 mt-1 mb-4">
|
<div className="grid gap-3 mt-1 mb-4">
|
||||||
<div className="grid grid-cols-4 items-center gap-4">
|
<div className="grid grid-cols-4 items-center gap-4">
|
||||||
<Label htmlFor="name" className="text-right">
|
<Label htmlFor="name" className="text-end">
|
||||||
{t("add_system.name")}
|
{t("add_system.name")}
|
||||||
</Label>
|
</Label>
|
||||||
<Input id="name" name="name" className="col-span-3" required />
|
<Input id="name" name="name" className="col-span-3" required />
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-4 items-center gap-4">
|
<div className="grid grid-cols-4 items-center gap-4">
|
||||||
<Label htmlFor="host" className="text-right">
|
<Label htmlFor="host" className="text-end">
|
||||||
{t("add_system.host_ip")}
|
{t("add_system.host_ip")}
|
||||||
</Label>
|
</Label>
|
||||||
<Input id="host" name="host" className="col-span-3" required />
|
<Input id="host" name="host" className="col-span-3" required />
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-4 items-center gap-4">
|
<div className="grid grid-cols-4 items-center gap-4">
|
||||||
<Label htmlFor="port" className="text-right">
|
<Label htmlFor="port" className="text-end">
|
||||||
{t("add_system.port")}
|
{t("add_system.port")}
|
||||||
</Label>
|
</Label>
|
||||||
<Input ref={port} name="port" id="port" defaultValue="45876" className="col-span-3" required />
|
<Input ref={port} name="port" id="port" defaultValue="45876" className="col-span-3" required />
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-4 items-center gap-4 relative">
|
<div className="grid grid-cols-4 items-center gap-4 relative">
|
||||||
<Label htmlFor="pkey" className="text-right whitespace-pre">
|
<Label htmlFor="pkey" className="text-end whitespace-pre">
|
||||||
{t("add_system.key")}
|
{t("add_system.key")}
|
||||||
</Label>
|
</Label>
|
||||||
<Input readOnly id="pkey" value={publicKey} className="col-span-3" required></Input>
|
<Input readOnly id="pkey" value={publicKey} className="col-span-3" required></Input>
|
||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
"h-6 w-24 bg-gradient-to-r from-transparent to-background to-65% absolute right-1 pointer-events-none"
|
"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>
|
></div>
|
||||||
<TooltipProvider delayDuration={100}>
|
<TooltipProvider delayDuration={100}>
|
||||||
@@ -137,7 +136,7 @@ export function AddSystemButton({ className }: { className?: string }) {
|
|||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant={"link"}
|
variant={"link"}
|
||||||
className="absolute right-0"
|
className="absolute end-0"
|
||||||
onClick={() => copyToClipboard(publicKey)}
|
onClick={() => copyToClipboard(publicKey)}
|
||||||
>
|
>
|
||||||
<Copy className="h-4 w-4 " />
|
<Copy className="h-4 w-4 " />
|
||||||
@@ -152,7 +151,7 @@ export function AddSystemButton({ className }: { className?: string }) {
|
|||||||
</div>
|
</div>
|
||||||
{/* Docker */}
|
{/* Docker */}
|
||||||
<TabsContent value="docker">
|
<TabsContent value="docker">
|
||||||
<DialogFooter className="flex justify-end gap-2 sm:w-[calc(100%+20px)] sm:-ml-[20px]">
|
<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)}>
|
<Button type="button" variant={"ghost"} onClick={() => copyDockerCompose(port.current.value)}>
|
||||||
{t("copy")} docker compose
|
{t("copy")} docker compose
|
||||||
</Button>
|
</Button>
|
||||||
@@ -161,7 +160,7 @@ export function AddSystemButton({ className }: { className?: string }) {
|
|||||||
</TabsContent>
|
</TabsContent>
|
||||||
{/* Binary */}
|
{/* Binary */}
|
||||||
<TabsContent value="binary">
|
<TabsContent value="binary">
|
||||||
<DialogFooter className="flex justify-end gap-2 sm:w-[calc(100%+20px)] sm:-ml-[20px]">
|
<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)}>
|
<Button type="button" variant={"ghost"} onClick={() => copyInstallCommand(port.current.value)}>
|
||||||
{t("copy")} linux {t("add_system.command")}
|
{t("copy")} linux {t("add_system.command")}
|
||||||
</Button>
|
</Button>
|
||||||
|
@@ -78,11 +78,11 @@ function TheContent({
|
|||||||
<Tabs defaultValue="system">
|
<Tabs defaultValue="system">
|
||||||
<TabsList className="mb-1 -mt-0.5">
|
<TabsList className="mb-1 -mt-0.5">
|
||||||
<TabsTrigger value="system">
|
<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}
|
{system.name}
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger value="global">
|
<TabsTrigger value="global">
|
||||||
<GlobeIcon className="mr-1.5 h-3.5 w-3.5" />
|
<GlobeIcon className="me-1.5 h-3.5 w-3.5" />
|
||||||
{t("all_systems")}
|
{t("all_systems")}
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
@@ -59,7 +59,7 @@ export default function CommandPalette({ open, setOpen }: { open: boolean; setOp
|
|||||||
setOpen(false)
|
setOpen(false)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Server className="mr-2 h-4 w-4" />
|
<Server className="me-2 h-4 w-4" />
|
||||||
<span>{system.name}</span>
|
<span>{system.name}</span>
|
||||||
<CommandShortcut>{system.host}</CommandShortcut>
|
<CommandShortcut>{system.host}</CommandShortcut>
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
@@ -76,7 +76,7 @@ export default function CommandPalette({ open, setOpen }: { open: boolean; setOp
|
|||||||
setOpen(false)
|
setOpen(false)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<LayoutDashboard className="mr-2 h-4 w-4" />
|
<LayoutDashboard className="me-2 h-4 w-4" />
|
||||||
<span>{t("command.dashboard")}</span>
|
<span>{t("command.dashboard")}</span>
|
||||||
<CommandShortcut>{t("command.page")}</CommandShortcut>
|
<CommandShortcut>{t("command.page")}</CommandShortcut>
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
@@ -86,7 +86,7 @@ export default function CommandPalette({ open, setOpen }: { open: boolean; setOp
|
|||||||
setOpen(false)
|
setOpen(false)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SettingsIcon className="mr-2 h-4 w-4" />
|
<SettingsIcon className="me-2 h-4 w-4" />
|
||||||
<span>{t("settings.settings")}</span>
|
<span>{t("settings.settings")}</span>
|
||||||
<CommandShortcut>{t("settings.settings")}</CommandShortcut>
|
<CommandShortcut>{t("settings.settings")}</CommandShortcut>
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
@@ -97,7 +97,7 @@ export default function CommandPalette({ open, setOpen }: { open: boolean; setOp
|
|||||||
setOpen(false)
|
setOpen(false)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MailIcon className="mr-2 h-4 w-4" />
|
<MailIcon className="me-2 h-4 w-4" />
|
||||||
<span>{t("settings.notifications.title")}</span>
|
<span>{t("settings.notifications.title")}</span>
|
||||||
<CommandShortcut>{t("settings.settings")}</CommandShortcut>
|
<CommandShortcut>{t("settings.settings")}</CommandShortcut>
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
@@ -107,7 +107,7 @@ export default function CommandPalette({ open, setOpen }: { open: boolean; setOp
|
|||||||
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" />
|
<Github className="me-2 h-4 w-4" />
|
||||||
<span>{t("command.documentation")}</span>
|
<span>{t("command.documentation")}</span>
|
||||||
<CommandShortcut>GitHub</CommandShortcut>
|
<CommandShortcut>GitHub</CommandShortcut>
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
@@ -123,7 +123,7 @@ export default function CommandPalette({ open, setOpen }: { open: boolean; setOp
|
|||||||
window.open("/_/", "_blank")
|
window.open("/_/", "_blank")
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<UsersIcon className="mr-2 h-4 w-4" />
|
<UsersIcon className="me-2 h-4 w-4" />
|
||||||
<span>{t("user_dm.users")}</span>
|
<span>{t("user_dm.users")}</span>
|
||||||
<CommandShortcut>{t("command.admin")}</CommandShortcut>
|
<CommandShortcut>{t("command.admin")}</CommandShortcut>
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
@@ -133,7 +133,7 @@ export default function CommandPalette({ open, setOpen }: { open: boolean; setOp
|
|||||||
window.open("/_/#/logs", "_blank")
|
window.open("/_/#/logs", "_blank")
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<LogsIcon className="mr-2 h-4 w-4" />
|
<LogsIcon className="me-2 h-4 w-4" />
|
||||||
<span>{t("user_dm.logs")}</span>
|
<span>{t("user_dm.logs")}</span>
|
||||||
<CommandShortcut>{t("command.admin")}</CommandShortcut>
|
<CommandShortcut>{t("command.admin")}</CommandShortcut>
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
@@ -143,7 +143,7 @@ export default function CommandPalette({ open, setOpen }: { open: boolean; setOp
|
|||||||
window.open("/_/#/settings/backups", "_blank")
|
window.open("/_/#/settings/backups", "_blank")
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DatabaseBackupIcon className="mr-2 h-4 w-4" />
|
<DatabaseBackupIcon className="me-2 h-4 w-4" />
|
||||||
<span>{t("user_dm.backups")}</span>
|
<span>{t("user_dm.backups")}</span>
|
||||||
<CommandShortcut>{t("command.admin")}</CommandShortcut>
|
<CommandShortcut>{t("command.admin")}</CommandShortcut>
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
@@ -154,7 +154,7 @@ export default function CommandPalette({ open, setOpen }: { open: boolean; setOp
|
|||||||
window.open("/_/#/settings/auth-providers", "_blank")
|
window.open("/_/#/settings/auth-providers", "_blank")
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<LockKeyholeIcon className="mr-2 h-4 w-4" />
|
<LockKeyholeIcon className="me-2 h-4 w-4" />
|
||||||
<span>{t("user_dm.auth_providers")}</span>
|
<span>{t("user_dm.auth_providers")}</span>
|
||||||
<CommandShortcut>{t("command.admin")}</CommandShortcut>
|
<CommandShortcut>{t("command.admin")}</CommandShortcut>
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
@@ -165,7 +165,7 @@ export default function CommandPalette({ open, setOpen }: { open: boolean; setOp
|
|||||||
window.open("/_/#/settings/mail", "_blank")
|
window.open("/_/#/settings/mail", "_blank")
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MailIcon className="mr-2 h-4 w-4" />
|
<MailIcon className="me-2 h-4 w-4" />
|
||||||
<span>{t("command.SMTP_settings")}</span>
|
<span>{t("command.SMTP_settings")}</span>
|
||||||
<CommandShortcut>{t("command.admin")}</CommandShortcut>
|
<CommandShortcut>{t("command.admin")}</CommandShortcut>
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
|
@@ -2,7 +2,16 @@ export function Logo({ className }: { className?: string }) {
|
|||||||
return (
|
return (
|
||||||
// Righteous
|
// Righteous
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 285 75" className={className}>
|
<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>
|
</svg>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -38,13 +38,12 @@ export default function Navbar() {
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
return (
|
return (
|
||||||
<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">
|
<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"}>
|
<Link href="/" aria-label="Home" className="p-2 pl-0 me-3">
|
||||||
<Logo className="h-[1.15rem] md:h-[1.25em] fill-foreground" />
|
<Logo className="h-[1.1rem] md:h-5 fill-foreground" />
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<SearchButton t={t} />
|
<SearchButton t={t} />
|
||||||
|
|
||||||
<div className={"flex ml-auto items-center"}>
|
<div className="flex items-center ms-auto">
|
||||||
<LangToggle />
|
<LangToggle />
|
||||||
<ModeToggle />
|
<ModeToggle />
|
||||||
<Link
|
<Link
|
||||||
@@ -106,7 +105,7 @@ export default function Navbar() {
|
|||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
<AddSystemButton className="ml-2" />
|
<AddSystemButton className="ms-2" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -125,14 +124,14 @@ function SearchButton({ t }: { t: TFunction<"translation", undefined> }) {
|
|||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="hidden md:block text-sm text-muted-foreground px-4 mx-3"
|
className="hidden md:block text-sm text-muted-foreground px-4"
|
||||||
onClick={() => setOpen(true)}
|
onClick={() => setOpen(true)}
|
||||||
>
|
>
|
||||||
<span className="flex items-center">
|
<span className="flex items-center">
|
||||||
<SearchIcon className="mr-1.5 h-4 w-4" />
|
<SearchIcon className="me-1.5 h-4 w-4" />
|
||||||
{t("search")}
|
{t("search")}
|
||||||
<span className="sr-only">{t("search")}</span>
|
<span className="sr-only">{t("search")}</span>
|
||||||
<span className="flex items-center ml-3.5">
|
<span className="flex items-center ms-3.5">
|
||||||
<Kbd>{isMac ? "⌘" : "Ctrl"}</Kbd>
|
<Kbd>{isMac ? "⌘" : "Ctrl"}</Kbd>
|
||||||
<Kbd>K</Kbd>
|
<Kbd>K</Kbd>
|
||||||
</span>
|
</span>
|
||||||
|
@@ -110,7 +110,7 @@ export default function () {
|
|||||||
<Input
|
<Input
|
||||||
placeholder={t("filter")}
|
placeholder={t("filter")}
|
||||||
onChange={(e) => setFilter(e.target.value)}
|
onChange={(e) => setFilter(e.target.value)}
|
||||||
className="w-full md:w-56 lg:w-72 ml-auto px-4"
|
className="w-full md:w-56 lg:w-72 ms-auto px-4"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
@@ -122,7 +122,7 @@ export default function () {
|
|||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{hubVersion && (
|
{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
|
<a
|
||||||
href="https://github.com/henrygd/beszel"
|
href="https://github.com/henrygd/beszel"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
@@ -66,6 +66,7 @@ export default function ConfigYaml() {
|
|||||||
</div>
|
</div>
|
||||||
{configContent && (
|
{configContent && (
|
||||||
<Textarea
|
<Textarea
|
||||||
|
dir="ltr"
|
||||||
autoFocus
|
autoFocus
|
||||||
defaultValue={configContent}
|
defaultValue={configContent}
|
||||||
spellCheck="false"
|
spellCheck="false"
|
||||||
|
@@ -78,8 +78,12 @@ function CellFormatter(info: CellContext<SystemRecord, unknown>) {
|
|||||||
|
|
||||||
function sortableHeader(column: Column<SystemRecord, unknown>, name: string, Icon: any, hideSortIcon = false) {
|
function sortableHeader(column: Column<SystemRecord, unknown>, name: string, Icon: any, hideSortIcon = false) {
|
||||||
return (
|
return (
|
||||||
<Button variant="ghost" className="h-9 px-3" onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}>
|
<Button
|
||||||
<Icon className="mr-2 h-4 w-4" />
|
variant="ghost"
|
||||||
|
className="h-9 px-3 rtl:ml-auto flex"
|
||||||
|
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||||
|
>
|
||||||
|
<Icon className="me-2 h-4 w-4" />
|
||||||
{name}
|
{name}
|
||||||
{!hideSortIcon && <ArrowUpDownIcon className="ml-2 h-4 w-4" />}
|
{!hideSortIcon && <ArrowUpDownIcon className="ml-2 h-4 w-4" />}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -110,7 +114,7 @@ export default function SystemsTable({ filter }: { filter?: string }) {
|
|||||||
cell: (info) => {
|
cell: (info) => {
|
||||||
const { status } = info.row.original
|
const { status } = info.row.original
|
||||||
return (
|
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
|
<span
|
||||||
className={cn("w-2 h-2 left-0 rounded-full", {
|
className={cn("w-2 h-2 left-0 rounded-full", {
|
||||||
"bg-green-500": status === "up",
|
"bg-green-500": status === "up",
|
||||||
@@ -176,7 +180,7 @@ export default function SystemsTable({ filter }: { filter?: string }) {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return (
|
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
|
<span
|
||||||
className={cn("w-2 h-2 left-0 rounded-full", version === hubVersion ? "bg-green-500" : "bg-yellow-500")}
|
className={cn("w-2 h-2 left-0 rounded-full", version === hubVersion ? "bg-green-500" : "bg-yellow-500")}
|
||||||
style={{ marginBottom: "-1px" }}
|
style={{ marginBottom: "-1px" }}
|
||||||
|
@@ -44,12 +44,12 @@ const AlertDialogContent = React.forwardRef<
|
|||||||
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
|
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
|
||||||
|
|
||||||
const AlertDialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
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>) => (
|
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"
|
||||||
|
|
||||||
|
@@ -40,7 +40,7 @@ const CommandInput = React.forwardRef<
|
|||||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
|
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
|
||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<div className="flex items-center border-b px-3" cmdk-input-wrapper="">
|
<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
|
<CommandPrimitive.Input
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
@@ -115,7 +115,7 @@ const CommandItem = React.forwardRef<
|
|||||||
CommandItem.displayName = CommandPrimitive.Item.displayName
|
CommandItem.displayName = CommandPrimitive.Item.displayName
|
||||||
|
|
||||||
const CommandShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
|
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"
|
||||||
|
|
||||||
|
@@ -42,7 +42,7 @@ const DialogContent = React.forwardRef<
|
|||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{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" />
|
<X className="h-4 w-4" />
|
||||||
<span className="sr-only">Close</span>
|
<span className="sr-only">Close</span>
|
||||||
</DialogPrimitive.Close>
|
</DialogPrimitive.Close>
|
||||||
@@ -52,12 +52,12 @@ const DialogContent = React.forwardRef<
|
|||||||
DialogContent.displayName = DialogPrimitive.Content.displayName
|
DialogContent.displayName = DialogPrimitive.Content.displayName
|
||||||
|
|
||||||
const DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
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>) => (
|
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"
|
||||||
|
|
||||||
|
@@ -17,7 +17,7 @@ const Switch = React.forwardRef<
|
|||||||
>
|
>
|
||||||
<SwitchPrimitives.Thumb
|
<SwitchPrimitives.Thumb
|
||||||
className={cn(
|
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"
|
"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>
|
</SwitchPrimitives.Root>
|
||||||
|
@@ -46,7 +46,7 @@ const TableHead = React.forwardRef<HTMLTableCellElement, React.ThHTMLAttributes<
|
|||||||
<th
|
<th
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
|
"h-12 px-4 text-start align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import i18n from "i18next"
|
import i18n from "i18next"
|
||||||
import { initReactI18next } from "react-i18next"
|
import { initReactI18next } from "react-i18next"
|
||||||
import enTranslations from "../locales/en/translation.json"
|
import enTranslations from "../locales/en/translation.json"
|
||||||
|
import { $direction } from "./stores"
|
||||||
|
|
||||||
// Custom language detector to use localStorage
|
// Custom language detector to use localStorage
|
||||||
const languageDetector: any = {
|
const languageDetector: any = {
|
||||||
@@ -62,6 +63,9 @@ export async function setLang(locale: string) {
|
|||||||
const messages = await loadMessages(locale)
|
const messages = await loadMessages(locale)
|
||||||
i18n.addResourceBundle(locale, "translation", messages)
|
i18n.addResourceBundle(locale, "translation", messages)
|
||||||
await i18n.changeLanguage(locale)
|
await i18n.changeLanguage(locale)
|
||||||
|
const dir = i18n.dir(locale)
|
||||||
|
document.dir = dir
|
||||||
|
$direction.set(dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize with detected/saved language
|
// Initialize with detected/saved language
|
||||||
|
@@ -1,4 +1,8 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"lang": "ar",
|
||||||
|
"label": "العربية"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"lang": "de",
|
"lang": "de",
|
||||||
"label": "Deutsch"
|
"label": "Deutsch"
|
||||||
|
@@ -39,3 +39,6 @@ export const $containerFilter = atom("")
|
|||||||
|
|
||||||
/** Fallback copy to clipboard dialog content */
|
/** Fallback copy to clipboard dialog content */
|
||||||
export const $copyContent = atom("")
|
export const $copyContent = atom("")
|
||||||
|
|
||||||
|
/** Direction for localization */
|
||||||
|
export const $direction = atom<"ltr" | "rtl">("ltr")
|
||||||
|
@@ -8,30 +8,30 @@
|
|||||||
"systems": "الأنظمة",
|
"systems": "الأنظمة",
|
||||||
"cancel": "إلغاء",
|
"cancel": "إلغاء",
|
||||||
"continue": "متابعة",
|
"continue": "متابعة",
|
||||||
"minutes_zero": "",
|
"minutes_zero": "٠ دقائق",
|
||||||
"minutes_one": "{{count}} دقيقة",
|
"minutes_one": "{{count}} دقيقة",
|
||||||
"minutes_two": "",
|
"minutes_two": "{{count}} دقيقتان",
|
||||||
"minutes_few": "",
|
"minutes_few": "{{count}} دقائق",
|
||||||
"minutes_many": "",
|
"minutes_many": "{{count}} دقيقة",
|
||||||
"minutes_other": "{{count}} دقائق",
|
"minutes_other": "{{count}} دقائق",
|
||||||
"hours_zero": "",
|
"hours_zero": "٠ ساعات",
|
||||||
"hours_one": "{{count}} ساعة",
|
"hours_one": "{{count}} ساعة",
|
||||||
"hours_two": "",
|
"hours_two": "{{count}} ساعتان",
|
||||||
"hours_few": "",
|
"hours_few": "{{count}} ساعات",
|
||||||
"hours_many": "",
|
"hours_many": "{{count}} ساعة",
|
||||||
"hours_other": "{{count}} ساعات",
|
"hours_other": "{{count}} ساعات",
|
||||||
"days_zero": "",
|
"days_zero": "٠ أيام",
|
||||||
"days_one": "",
|
"days_one": "{{count}} يوم",
|
||||||
"days_two": "",
|
"days_two": "{{count}} يومان",
|
||||||
"days_few": "",
|
"days_few": "{{count}} أيام",
|
||||||
"days_many": "",
|
"days_many": "{{count}} يومًا",
|
||||||
"days_other": "{{count}} أيام",
|
"days_other": "{{count}} أيام",
|
||||||
"weeks_zero": "",
|
"weeks_zero": "٠ أسابيع",
|
||||||
"weeks_one": "{{count}} أسبوع",
|
"weeks_one": "{{count}} أسبوع",
|
||||||
"weeks_two": "",
|
"weeks_two": "{{count}} أسبوعان",
|
||||||
"weeks_few": "",
|
"weeks_few": "{{count}} أسابيع",
|
||||||
"weeks_many": "",
|
"weeks_many": "{{count}} أسبوعًا",
|
||||||
"weeks_other": "",
|
"weeks_other": "{{count}} أسابيع",
|
||||||
"home": {
|
"home": {
|
||||||
"active_alerts": "التنبيهات النشطة",
|
"active_alerts": "التنبيهات النشطة",
|
||||||
"active_des": "يتجاوز {{value}}{{unit}} في آخر ",
|
"active_des": "يتجاوز {{value}}{{unit}} في آخر ",
|
||||||
@@ -71,8 +71,8 @@
|
|||||||
"disk_usage_des": "يتم التشغيل عندما يتجاوز استخدام أي قرص الحد.",
|
"disk_usage_des": "يتم التشغيل عندما يتجاوز استخدام أي قرص الحد.",
|
||||||
"bandwidth": "النطاق الترددي",
|
"bandwidth": "النطاق الترددي",
|
||||||
"bandwidth_des": "يتم التشغيل عندما يتجاوز استخدام الشبكة الحد.",
|
"bandwidth_des": "يتم التشغيل عندما يتجاوز استخدام الشبكة الحد.",
|
||||||
"temperature": "",
|
"temperature": "درجة الحرارة",
|
||||||
"temperature_des": ""
|
"temperature_des": "يتم التشغيل عندما يتجاوز أي مستشعر الحد المسموح به."
|
||||||
},
|
},
|
||||||
"average_exceeds": "المتوسط يتجاوز",
|
"average_exceeds": "المتوسط يتجاوز",
|
||||||
"for": "لمدة"
|
"for": "لمدة"
|
||||||
|
@@ -4,7 +4,8 @@ import { Suspense, lazy, useEffect } from "react"
|
|||||||
import ReactDOM from "react-dom/client"
|
import ReactDOM from "react-dom/client"
|
||||||
import Home from "./components/routes/home.tsx"
|
import Home from "./components/routes/home.tsx"
|
||||||
import { ThemeProvider } from "./components/theme-provider.tsx"
|
import { ThemeProvider } from "./components/theme-provider.tsx"
|
||||||
import { $authenticated, $systems, pb, $publicKey, $hubVersion, $copyContent } from "./lib/stores.ts"
|
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 { updateUserSettings, updateAlerts, updateFavicon, updateSystemList } from "./lib/utils.ts"
|
||||||
import { useStore } from "@nanostores/react"
|
import { useStore } from "@nanostores/react"
|
||||||
import { Toaster } from "./components/ui/toaster.tsx"
|
import { Toaster } from "./components/ui/toaster.tsx"
|
||||||
@@ -77,16 +78,15 @@ const App = () => {
|
|||||||
const Layout = () => {
|
const Layout = () => {
|
||||||
const authenticated = useStore($authenticated)
|
const authenticated = useStore($authenticated)
|
||||||
const copyContent = useStore($copyContent)
|
const copyContent = useStore($copyContent)
|
||||||
|
const direction = useStore($direction)
|
||||||
|
|
||||||
if (!authenticated) {
|
|
||||||
return (
|
return (
|
||||||
|
<DirectionProvider dir={direction}>
|
||||||
|
{!authenticated ? (
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<LoginPage />
|
<LoginPage />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
)
|
) : (
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
<>
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<Navbar />
|
<Navbar />
|
||||||
@@ -100,6 +100,8 @@ const Layout = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
)}
|
||||||
|
</DirectionProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -92,6 +92,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
require("tailwindcss-animate"),
|
require("tailwindcss-animate"),
|
||||||
|
require("tailwindcss-rtl"),
|
||||||
function ({ addVariant }) {
|
function ({ addVariant }) {
|
||||||
addVariant("light", ".light &")
|
addVariant("light", ".light &")
|
||||||
},
|
},
|
||||||
|
Reference in New Issue
Block a user