mirror of
https://github.com/henrygd/beszel.git
synced 2025-10-30 10:07:02 +00:00
formatting (biome)
This commit is contained in:
@@ -78,34 +78,3 @@ func (t *DeltaTracker[K, V]) Cycle() {
|
|||||||
t.previous = t.current
|
t.previous = t.current
|
||||||
t.current = make(map[K]V)
|
t.current = make(map[K]V)
|
||||||
}
|
}
|
||||||
|
|
||||||
// // --- Example 1: Integer values (unchanged) ---
|
|
||||||
// fmt.Println("--- 🚀 Example with int64 values (PIDs) ---")
|
|
||||||
// pidTracker := NewDeltaTracker[int, int64]()
|
|
||||||
// pidTracker.Set(101, 20000)
|
|
||||||
// pidTracker.Cycle()
|
|
||||||
// pidTracker.Set(101, 22500)
|
|
||||||
// fmt.Println("PID Deltas:", pidTracker.Deltas())
|
|
||||||
// fmt.Println("----------------------------------------")
|
|
||||||
|
|
||||||
// // --- Example 2: Float values (New!) ---
|
|
||||||
// fmt.Println("\n--- 🚀 Example with float64 values (CPU Load) ---")
|
|
||||||
// // Track the 1-minute load average for different servers.
|
|
||||||
// loadTracker := NewDeltaTracker[string, float64]()
|
|
||||||
|
|
||||||
// // Minute 1
|
|
||||||
// loadTracker.Set("server-alpha", 0.74)
|
|
||||||
// loadTracker.Set("server-beta", 1.15)
|
|
||||||
// fmt.Println("Minute 1 Loads:", loadTracker.Deltas())
|
|
||||||
// loadTracker.Cycle()
|
|
||||||
|
|
||||||
// // Minute 2
|
|
||||||
// loadTracker.Set("server-alpha", 0.68) // Load decreased
|
|
||||||
// loadTracker.Set("server-beta", 1.55) // Load increased
|
|
||||||
// loadTracker.Set("server-gamma", 0.25) // New server
|
|
||||||
|
|
||||||
// minute2Deltas := loadTracker.Deltas()
|
|
||||||
// fmt.Println("Minute 2 Load Deltas:", minute2Deltas)
|
|
||||||
// fmt.Printf("Change in alpha's load: %.2f\n", minute2Deltas["server-alpha"])
|
|
||||||
// fmt.Printf("Change in beta's load: %.2f\n", minute2Deltas["server-beta"])
|
|
||||||
// fmt.Println("----------------------------------------")
|
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import { Trans } from "@lingui/react/macro"
|
|
||||||
import { t } from "@lingui/core/macro"
|
import { t } from "@lingui/core/macro"
|
||||||
|
import { Trans } from "@lingui/react/macro"
|
||||||
|
import { useStore } from "@nanostores/react"
|
||||||
|
import { getPagePath } from "@nanostores/router"
|
||||||
|
import { ChevronDownIcon, ExternalLinkIcon, PlusIcon } from "lucide-react"
|
||||||
|
import { memo, useEffect, useRef, useState } from "react"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
@@ -10,34 +14,30 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog"
|
} from "@/components/ui/dialog"
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
|
||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/components/ui/input"
|
||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/components/ui/label"
|
||||||
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||||||
|
import { isReadOnlyUser, pb } from "@/lib/api"
|
||||||
|
import { SystemStatus } from "@/lib/enums"
|
||||||
import { $publicKey } from "@/lib/stores"
|
import { $publicKey } from "@/lib/stores"
|
||||||
import { cn, generateToken, tokenMap, useBrowserStorage } from "@/lib/utils"
|
import { cn, generateToken, tokenMap, useBrowserStorage } from "@/lib/utils"
|
||||||
import { pb, isReadOnlyUser } from "@/lib/api"
|
import type { SystemRecord } from "@/types"
|
||||||
import { useStore } from "@nanostores/react"
|
|
||||||
import { ChevronDownIcon, ExternalLinkIcon, PlusIcon } from "lucide-react"
|
|
||||||
import { memo, useEffect, useRef, useState } from "react"
|
|
||||||
import { $router, basePath, Link, navigate } from "./router"
|
|
||||||
import { SystemRecord } from "@/types"
|
|
||||||
import { SystemStatus } from "@/lib/enums"
|
|
||||||
import { AppleIcon, DockerIcon, FreeBsdIcon, TuxIcon, WindowsIcon } from "./ui/icons"
|
|
||||||
import { InputCopy } from "./ui/input-copy"
|
|
||||||
import { getPagePath } from "@nanostores/router"
|
|
||||||
import {
|
import {
|
||||||
copyDockerCompose,
|
copyDockerCompose,
|
||||||
copyDockerRun,
|
copyDockerRun,
|
||||||
copyLinuxCommand,
|
copyLinuxCommand,
|
||||||
copyWindowsCommand,
|
copyWindowsCommand,
|
||||||
DropdownItem,
|
type DropdownItem,
|
||||||
InstallDropdown,
|
InstallDropdown,
|
||||||
} from "./install-dropdowns"
|
} from "./install-dropdowns"
|
||||||
|
import { $router, basePath, Link, navigate } from "./router"
|
||||||
import { DropdownMenu, DropdownMenuTrigger } from "./ui/dropdown-menu"
|
import { DropdownMenu, DropdownMenuTrigger } from "./ui/dropdown-menu"
|
||||||
|
import { AppleIcon, DockerIcon, FreeBsdIcon, TuxIcon, WindowsIcon } from "./ui/icons"
|
||||||
|
import { InputCopy } from "./ui/input-copy"
|
||||||
|
|
||||||
export function AddSystemButton({ className }: { className?: string }) {
|
export function AddSystemButton({ className }: { className?: string }) {
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
let opened = useRef(false)
|
const opened = useRef(false)
|
||||||
if (open) {
|
if (open) {
|
||||||
opened.current = true
|
opened.current = true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { ColumnDef } from "@tanstack/react-table"
|
|
||||||
import { AlertsHistoryRecord } from "@/types"
|
|
||||||
import { Button } from "@/components/ui/button"
|
|
||||||
import { Badge } from "@/components/ui/badge"
|
|
||||||
import { formatShortDate, toFixedFloat, formatDuration, cn } from "@/lib/utils"
|
|
||||||
import { alertInfo } from "@/lib/alerts"
|
|
||||||
import { Trans } from "@lingui/react/macro"
|
|
||||||
import { t } from "@lingui/core/macro"
|
import { t } from "@lingui/core/macro"
|
||||||
|
import { Trans } from "@lingui/react/macro"
|
||||||
|
import type { ColumnDef } from "@tanstack/react-table"
|
||||||
|
import { Badge } from "@/components/ui/badge"
|
||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
import { alertInfo } from "@/lib/alerts"
|
||||||
|
import { cn, formatDuration, formatShortDate, toFixedFloat } from "@/lib/utils"
|
||||||
|
import type { AlertsHistoryRecord } from "@/types"
|
||||||
|
|
||||||
export const alertsHistoryColumns: ColumnDef<AlertsHistoryRecord>[] = [
|
export const alertsHistoryColumns: ColumnDef<AlertsHistoryRecord>[] = [
|
||||||
{
|
{
|
||||||
@@ -38,7 +38,7 @@ export const alertsHistoryColumns: ColumnDef<AlertsHistoryRecord>[] = [
|
|||||||
</Button>
|
</Button>
|
||||||
),
|
),
|
||||||
cell: ({ getValue, row }) => {
|
cell: ({ getValue, row }) => {
|
||||||
let name = getValue() as string
|
const name = getValue() as string
|
||||||
const info = alertInfo[row.original.name]
|
const info = alertInfo[row.original.name]
|
||||||
const Icon = info?.icon
|
const Icon = info?.icon
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { t } from "@lingui/core/macro"
|
import { t } from "@lingui/core/macro"
|
||||||
import { memo, useMemo, useState } from "react"
|
|
||||||
import { useStore } from "@nanostores/react"
|
import { useStore } from "@nanostores/react"
|
||||||
import { $alerts } from "@/lib/stores"
|
|
||||||
import { BellIcon } from "lucide-react"
|
import { BellIcon } from "lucide-react"
|
||||||
import { cn } from "@/lib/utils"
|
import { memo, useMemo, useState } from "react"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { SystemRecord } from "@/types"
|
|
||||||
import { AlertDialogContent } from "./alerts-sheet"
|
|
||||||
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet"
|
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet"
|
||||||
|
import { $alerts } from "@/lib/stores"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import type { SystemRecord } from "@/types"
|
||||||
|
import { AlertDialogContent } from "./alerts-sheet"
|
||||||
|
|
||||||
export default memo(function AlertsButton({ system }: { system: SystemRecord }) {
|
export default memo(function AlertsButton({ system }: { system: SystemRecord }) {
|
||||||
const [opened, setOpened] = useState(false)
|
const [opened, setOpened] = useState(false)
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
import { t } from "@lingui/core/macro"
|
import { t } from "@lingui/core/macro"
|
||||||
import { Trans, Plural } from "@lingui/react/macro"
|
import { Plural, Trans } from "@lingui/react/macro"
|
||||||
import { $alerts, $systems } from "@/lib/stores"
|
|
||||||
import { cn, debounce } from "@/lib/utils"
|
|
||||||
import { alertInfo } from "@/lib/alerts"
|
|
||||||
import { Switch } from "@/components/ui/switch"
|
|
||||||
import { AlertInfo, AlertRecord, SystemRecord } from "@/types"
|
|
||||||
import { lazy, memo, Suspense, useMemo, useState } from "react"
|
|
||||||
import { toast } from "@/components/ui/use-toast"
|
|
||||||
import { useStore } from "@nanostores/react"
|
import { useStore } from "@nanostores/react"
|
||||||
import { getPagePath } from "@nanostores/router"
|
import { getPagePath } from "@nanostores/router"
|
||||||
import { Checkbox } from "@/components/ui/checkbox"
|
import { GlobeIcon, ServerIcon } from "lucide-react"
|
||||||
import { DialogTitle, DialogDescription } from "@/components/ui/dialog"
|
import { lazy, memo, Suspense, useMemo, useState } from "react"
|
||||||
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"
|
|
||||||
import { ServerIcon, GlobeIcon } from "lucide-react"
|
|
||||||
import { $router, Link } from "@/components/router"
|
import { $router, Link } from "@/components/router"
|
||||||
import { DialogHeader } from "@/components/ui/dialog"
|
import { Checkbox } from "@/components/ui/checkbox"
|
||||||
|
import { DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"
|
||||||
|
import { Switch } from "@/components/ui/switch"
|
||||||
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||||||
|
import { toast } from "@/components/ui/use-toast"
|
||||||
|
import { alertInfo } from "@/lib/alerts"
|
||||||
import { pb } from "@/lib/api"
|
import { pb } from "@/lib/api"
|
||||||
|
import { $alerts, $systems } from "@/lib/stores"
|
||||||
|
import { cn, debounce } from "@/lib/utils"
|
||||||
|
import type { AlertInfo, AlertRecord, SystemRecord } from "@/types"
|
||||||
|
|
||||||
const Slider = lazy(() => import("@/components/ui/slider"))
|
const Slider = lazy(() => import("@/components/ui/slider"))
|
||||||
|
|
||||||
@@ -172,7 +171,7 @@ export function AlertContent({
|
|||||||
|
|
||||||
const [checked, setChecked] = useState(global ? false : !!alert)
|
const [checked, setChecked] = useState(global ? false : !!alert)
|
||||||
const [min, setMin] = useState(alert?.min || 10)
|
const [min, setMin] = useState(alert?.min || 10)
|
||||||
const [value, setValue] = useState(alert?.value || (singleDescription ? 0 : alertData.start ?? 80))
|
const [value, setValue] = useState(alert?.value || (singleDescription ? 0 : (alertData.start ?? 80)))
|
||||||
|
|
||||||
const Icon = alertData.icon
|
const Icon = alertData.icon
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
import { t } from "@lingui/core/macro"
|
||||||
|
import { Trans } from "@lingui/react/macro"
|
||||||
|
import { getPagePath } from "@nanostores/router"
|
||||||
|
import { DialogDescription } from "@radix-ui/react-dialog"
|
||||||
import {
|
import {
|
||||||
AlertOctagonIcon,
|
AlertOctagonIcon,
|
||||||
BookIcon,
|
BookIcon,
|
||||||
@@ -10,7 +14,7 @@ import {
|
|||||||
SettingsIcon,
|
SettingsIcon,
|
||||||
UsersIcon,
|
UsersIcon,
|
||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
|
import { memo, useEffect, useMemo } from "react"
|
||||||
import {
|
import {
|
||||||
CommandDialog,
|
CommandDialog,
|
||||||
CommandEmpty,
|
CommandEmpty,
|
||||||
@@ -21,15 +25,10 @@ import {
|
|||||||
CommandSeparator,
|
CommandSeparator,
|
||||||
CommandShortcut,
|
CommandShortcut,
|
||||||
} from "@/components/ui/command"
|
} from "@/components/ui/command"
|
||||||
import { memo, useEffect, useMemo } from "react"
|
import { isAdmin } from "@/lib/api"
|
||||||
import { $systems } from "@/lib/stores"
|
import { $systems } from "@/lib/stores"
|
||||||
import { getHostDisplayValue, listen } from "@/lib/utils"
|
import { getHostDisplayValue, listen } from "@/lib/utils"
|
||||||
import { $router, basePath, navigate, prependBasePath } from "./router"
|
import { $router, basePath, navigate, prependBasePath } from "./router"
|
||||||
import { Trans } from "@lingui/react/macro"
|
|
||||||
import { t } from "@lingui/core/macro"
|
|
||||||
import { getPagePath } from "@nanostores/router"
|
|
||||||
import { DialogDescription } from "@radix-ui/react-dialog"
|
|
||||||
import { isAdmin } from "@/lib/api"
|
|
||||||
|
|
||||||
export default memo(function CommandPalette({ open, setOpen }: { open: boolean; setOpen: (open: boolean) => void }) {
|
export default memo(function CommandPalette({ open, setOpen }: { open: boolean; setOpen: (open: boolean) => void }) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Trans } from "@lingui/react/macro";
|
import { Trans } from "@lingui/react/macro"
|
||||||
import { useEffect, useMemo, useRef } from "react"
|
import { useEffect, useMemo, useRef } from "react"
|
||||||
|
import { $copyContent } from "@/lib/stores"
|
||||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "./ui/dialog"
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "./ui/dialog"
|
||||||
import { Textarea } from "./ui/textarea"
|
import { Textarea } from "./ui/textarea"
|
||||||
import { $copyContent } from "@/lib/stores"
|
|
||||||
|
|
||||||
export default function CopyToClipboard({ content }: { content: string }) {
|
export default function CopyToClipboard({ content }: { content: string }) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { memo } from "react"
|
|
||||||
import { DropdownMenuContent, DropdownMenuItem } from "./ui/dropdown-menu"
|
|
||||||
import { copyToClipboard, getHubURL } from "@/lib/utils"
|
|
||||||
import { i18n } from "@lingui/core"
|
import { i18n } from "@lingui/core"
|
||||||
|
import { memo } from "react"
|
||||||
|
import { copyToClipboard, getHubURL } from "@/lib/utils"
|
||||||
|
import { DropdownMenuContent, DropdownMenuItem } from "./ui/dropdown-menu"
|
||||||
|
|
||||||
// const isbeta = beszel.hub_version.includes("beta")
|
// const isbeta = beszel.hub_version.includes("beta")
|
||||||
// const imagetag = isbeta ? ":edge" : ""
|
// const imagetag = isbeta ? ":edge" : ""
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
|
import { useLingui } from "@lingui/react/macro"
|
||||||
import { LanguagesIcon } from "lucide-react"
|
import { LanguagesIcon } from "lucide-react"
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
|
||||||
|
import { dynamicActivate } from "@/lib/i18n"
|
||||||
import languages from "@/lib/languages"
|
import languages from "@/lib/languages"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { useLingui } from "@lingui/react/macro"
|
|
||||||
import { dynamicActivate } from "@/lib/i18n"
|
|
||||||
|
|
||||||
export function LangToggle() {
|
export function LangToggle() {
|
||||||
const { i18n } = useLingui()
|
const { i18n } = useLingui()
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import { t } from "@lingui/core/macro"
|
import { t } from "@lingui/core/macro"
|
||||||
import { Trans } from "@lingui/react/macro"
|
import { Trans } from "@lingui/react/macro"
|
||||||
import { cn } from "@/lib/utils"
|
import { getPagePath } from "@nanostores/router"
|
||||||
|
import { KeyIcon, LoaderCircle, LockIcon, LogInIcon, MailIcon } from "lucide-react"
|
||||||
|
import type { AuthMethodsList, AuthProviderInfo, OAuth2AuthConfig } from "pocketbase"
|
||||||
|
import { useCallback, useEffect, useState } from "react"
|
||||||
|
import * as v from "valibot"
|
||||||
import { buttonVariants } from "@/components/ui/button"
|
import { buttonVariants } from "@/components/ui/button"
|
||||||
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"
|
||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/components/ui/input"
|
||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/components/ui/label"
|
||||||
import { KeyIcon, LoaderCircle, LockIcon, LogInIcon, MailIcon } from "lucide-react"
|
|
||||||
import { $authenticated } 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, useEffect, useState } from "react"
|
|
||||||
import { AuthMethodsList, AuthProviderInfo, OAuth2AuthConfig } from "pocketbase"
|
|
||||||
import { $router, Link, prependBasePath } from "../router"
|
|
||||||
import { getPagePath } from "@nanostores/router"
|
|
||||||
import { pb } from "@/lib/api"
|
import { pb } from "@/lib/api"
|
||||||
|
import { $authenticated } from "@/lib/stores"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { $router, Link, prependBasePath } from "../router"
|
||||||
|
import { toast } from "../ui/use-toast"
|
||||||
import { OtpInputForm } from "./otp-forms"
|
import { OtpInputForm } from "./otp-forms"
|
||||||
|
|
||||||
const honeypot = v.literal("")
|
const honeypot = v.literal("")
|
||||||
@@ -83,9 +83,9 @@ export function UserAuthForm({
|
|||||||
const result = v.safeParse(Schema, data)
|
const result = v.safeParse(Schema, data)
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
console.log(result)
|
console.log(result)
|
||||||
let errors = {}
|
const errors = {}
|
||||||
for (const issue of result.issues) {
|
for (const issue of result.issues) {
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
errors[issue.path[0].key] = issue.message
|
errors[issue.path[0].key] = issue.message
|
||||||
}
|
}
|
||||||
setErrors(errors)
|
setErrors(errors)
|
||||||
@@ -96,7 +96,7 @@ export function UserAuthForm({
|
|||||||
if (isFirstRun) {
|
if (isFirstRun) {
|
||||||
// check that passwords match
|
// check that passwords match
|
||||||
if (password !== passwordConfirm) {
|
if (password !== passwordConfirm) {
|
||||||
let msg = "Passwords do not match"
|
const msg = "Passwords do not match"
|
||||||
setErrors({ passwordConfirm: msg })
|
setErrors({ passwordConfirm: msg })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
import { Trans } from "@lingui/react/macro"
|
|
||||||
import { t } from "@lingui/core/macro"
|
import { t } from "@lingui/core/macro"
|
||||||
|
import { Trans } from "@lingui/react/macro"
|
||||||
import { LoaderCircle, MailIcon, SendHorizonalIcon } from "lucide-react"
|
import { LoaderCircle, MailIcon, SendHorizonalIcon } from "lucide-react"
|
||||||
|
import { useCallback, useState } from "react"
|
||||||
|
import { pb } from "@/lib/api"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { buttonVariants } from "../ui/button"
|
||||||
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "../ui/dialog"
|
||||||
import { Input } from "../ui/input"
|
import { Input } from "../ui/input"
|
||||||
import { Label } from "../ui/label"
|
import { Label } from "../ui/label"
|
||||||
import { useCallback, useState } from "react"
|
|
||||||
import { toast } from "../ui/use-toast"
|
import { toast } from "../ui/use-toast"
|
||||||
import { buttonVariants } from "../ui/button"
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
import { Dialog, DialogHeader } from "../ui/dialog"
|
|
||||||
import { DialogContent, DialogTrigger, DialogTitle } from "../ui/dialog"
|
|
||||||
import { pb } from "@/lib/api"
|
|
||||||
|
|
||||||
const showLoginFaliedToast = () => {
|
const showLoginFaliedToast = () => {
|
||||||
toast({
|
toast({
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { t } from "@lingui/core/macro"
|
import { t } from "@lingui/core/macro"
|
||||||
import { UserAuthForm } from "@/components/login/auth-form"
|
|
||||||
import { Logo } from "../logo"
|
|
||||||
import { useEffect, useMemo, useState } from "react"
|
|
||||||
import { useStore } from "@nanostores/react"
|
import { useStore } from "@nanostores/react"
|
||||||
import ForgotPassword from "./forgot-pass-form"
|
import type { AuthMethodsList } from "pocketbase"
|
||||||
import { $router } from "../router"
|
import { useEffect, useMemo, useState } from "react"
|
||||||
import { AuthMethodsList } from "pocketbase"
|
import { UserAuthForm } from "@/components/login/auth-form"
|
||||||
import { useTheme } from "../theme-provider"
|
|
||||||
import { pb } from "@/lib/api"
|
import { pb } from "@/lib/api"
|
||||||
|
import { Logo } from "../logo"
|
||||||
import { ModeToggle } from "../mode-toggle"
|
import { ModeToggle } from "../mode-toggle"
|
||||||
|
import { $router } from "../router"
|
||||||
|
import { useTheme } from "../theme-provider"
|
||||||
|
import ForgotPassword from "./forgot-pass-form"
|
||||||
import { OtpRequestForm } from "./otp-forms"
|
import { OtpRequestForm } from "./otp-forms"
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
@@ -53,7 +53,7 @@ export default function () {
|
|||||||
<div className="min-h-svh grid items-center py-12">
|
<div className="min-h-svh grid items-center py-12">
|
||||||
<div
|
<div
|
||||||
className="grid gap-5 w-full px-4 mx-auto"
|
className="grid gap-5 w-full px-4 mx-auto"
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
style={{ maxWidth: "21.5em", "--border": theme == "light" ? "hsl(30, 8%, 70%)" : "hsl(220, 3%, 25%)" }}
|
style={{ maxWidth: "21.5em", "--border": theme == "light" ? "hsl(30, 8%, 70%)" : "hsl(220, 3%, 25%)" }}
|
||||||
>
|
>
|
||||||
<div className="absolute top-3 right-3">
|
<div className="absolute top-3 right-3">
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
|
import { Trans } from "@lingui/react/macro"
|
||||||
|
import { LoaderCircle, MailIcon, SendHorizonalIcon } from "lucide-react"
|
||||||
import { useCallback, useState } from "react"
|
import { useCallback, useState } from "react"
|
||||||
|
import { InputOTP, InputOTPGroup, InputOTPSlot } from "@/components/ui/otp"
|
||||||
import { pb } from "@/lib/api"
|
import { pb } from "@/lib/api"
|
||||||
import { $authenticated } from "@/lib/stores"
|
import { $authenticated } from "@/lib/stores"
|
||||||
import { InputOTP, InputOTPGroup, InputOTPSlot } from "@/components/ui/otp"
|
|
||||||
import { Trans } from "@lingui/react/macro"
|
|
||||||
import { showLoginFaliedToast } from "./auth-form"
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { MailIcon, LoaderCircle, SendHorizonalIcon } from "lucide-react"
|
import { $router } from "../router"
|
||||||
import { Label } from "../ui/label"
|
|
||||||
import { buttonVariants } from "../ui/button"
|
import { buttonVariants } from "../ui/button"
|
||||||
import { Input } from "../ui/input"
|
import { Input } from "../ui/input"
|
||||||
import { $router } from "../router"
|
import { Label } from "../ui/label"
|
||||||
|
import { showLoginFaliedToast } from "./auth-form"
|
||||||
|
|
||||||
export function OtpInputForm({ otpId, mfaId }: { otpId: string; mfaId: string }) {
|
export function OtpInputForm({ otpId, mfaId }: { otpId: string; mfaId: string }) {
|
||||||
const [value, setValue] = useState("")
|
const [value, setValue] = useState("")
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { t } from "@lingui/core/macro"
|
import { t } from "@lingui/core/macro"
|
||||||
import { MoonStarIcon, SunIcon } from "lucide-react"
|
import { MoonStarIcon, SunIcon } from "lucide-react"
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button"
|
|
||||||
import { useTheme } from "@/components/theme-provider"
|
import { useTheme } from "@/components/theme-provider"
|
||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
|
||||||
export function ModeToggle() {
|
export function ModeToggle() {
|
||||||
const { theme, setTheme } = useTheme()
|
const { theme, setTheme } = useTheme()
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Trans } from "@lingui/react/macro"
|
import { Trans } from "@lingui/react/macro"
|
||||||
import { useState, lazy, Suspense } from "react"
|
import { getPagePath } from "@nanostores/router"
|
||||||
import { Button, buttonVariants } from "@/components/ui/button"
|
|
||||||
import {
|
import {
|
||||||
DatabaseBackupIcon,
|
DatabaseBackupIcon,
|
||||||
LogOutIcon,
|
LogOutIcon,
|
||||||
@@ -11,23 +10,24 @@ import {
|
|||||||
UserIcon,
|
UserIcon,
|
||||||
UsersIcon,
|
UsersIcon,
|
||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
import { $router, basePath, Link, prependBasePath } from "./router"
|
import { lazy, Suspense, useState } from "react"
|
||||||
import { LangToggle } from "./lang-toggle"
|
import { Button, buttonVariants } from "@/components/ui/button"
|
||||||
import { ModeToggle } from "./mode-toggle"
|
|
||||||
import { Logo } from "./logo"
|
|
||||||
import { cn, runOnce } from "@/lib/utils"
|
|
||||||
import { isReadOnlyUser, isAdmin, logOut, pb } from "@/lib/api"
|
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuTrigger,
|
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuLabel,
|
|
||||||
DropdownMenuSeparator,
|
|
||||||
DropdownMenuGroup,
|
DropdownMenuGroup,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
|
DropdownMenuLabel,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu"
|
} from "@/components/ui/dropdown-menu"
|
||||||
|
import { isAdmin, isReadOnlyUser, logOut, pb } from "@/lib/api"
|
||||||
|
import { cn, runOnce } from "@/lib/utils"
|
||||||
import { AddSystemButton } from "./add-system"
|
import { AddSystemButton } from "./add-system"
|
||||||
import { getPagePath } from "@nanostores/router"
|
import { LangToggle } from "./lang-toggle"
|
||||||
|
import { Logo } from "./logo"
|
||||||
|
import { ModeToggle } from "./mode-toggle"
|
||||||
|
import { $router, basePath, Link, prependBasePath } from "./router"
|
||||||
|
|
||||||
const CommandPalette = lazy(() => import("./command-palette"))
|
const CommandPalette = lazy(() => import("./command-palette"))
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export const prependBasePath = (path: string) => (basePath + path).replaceAll("/
|
|||||||
|
|
||||||
// prepend base path to routes
|
// prepend base path to routes
|
||||||
for (const route in routes) {
|
for (const route in routes) {
|
||||||
// @ts-ignore need as const above to get nanostores to parse types properly
|
// @ts-expect-error need as const above to get nanostores to parse types properly
|
||||||
routes[route] = prependBasePath(routes[route])
|
routes[route] = prependBasePath(routes[route])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ import { Trans, useLingui } from "@lingui/react/macro"
|
|||||||
import { redirectPage } from "@nanostores/router"
|
import { redirectPage } from "@nanostores/router"
|
||||||
import {
|
import {
|
||||||
CopyIcon,
|
CopyIcon,
|
||||||
|
ExternalLinkIcon,
|
||||||
FingerprintIcon,
|
FingerprintIcon,
|
||||||
KeyIcon,
|
KeyIcon,
|
||||||
MoreHorizontalIcon,
|
MoreHorizontalIcon,
|
||||||
RotateCwIcon,
|
RotateCwIcon,
|
||||||
ServerIcon,
|
ServerIcon,
|
||||||
Trash2Icon,
|
Trash2Icon,
|
||||||
ExternalLinkIcon,
|
|
||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
import { memo, useEffect, useMemo, useState } from "react"
|
import { memo, useEffect, useMemo, useState } from "react"
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { cn } from "@/lib/utils"
|
|
||||||
import { LoaderCircleIcon } from "lucide-react"
|
import { LoaderCircleIcon } from "lucide-react"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
export default function ({ msg, className }: { msg?: string; className?: string }) {
|
export default function ({ msg, className }: { msg?: string; className?: string }) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { SystemRecord } from "@/types"
|
import { t } from "@lingui/core/macro"
|
||||||
import { CellContext, ColumnDef, HeaderContext } from "@tanstack/react-table"
|
import { Trans, useLingui } from "@lingui/react/macro"
|
||||||
import { ClassValue } from "clsx"
|
import { useStore } from "@nanostores/react"
|
||||||
|
import { getPagePath } from "@nanostores/router"
|
||||||
|
import type { CellContext, ColumnDef, HeaderContext } from "@tanstack/react-table"
|
||||||
|
import type { ClassValue } from "clsx"
|
||||||
import {
|
import {
|
||||||
ArrowUpDownIcon,
|
ArrowUpDownIcon,
|
||||||
CopyIcon,
|
CopyIcon,
|
||||||
@@ -15,7 +18,10 @@ import {
|
|||||||
Trash2Icon,
|
Trash2Icon,
|
||||||
WifiIcon,
|
WifiIcon,
|
||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
import { Button } from "../ui/button"
|
import { memo, useMemo, useRef, useState } from "react"
|
||||||
|
import { isReadOnlyUser, pb } from "@/lib/api"
|
||||||
|
import { MeterState, SystemStatus } from "@/lib/enums"
|
||||||
|
import { $longestSystemNameLen, $userSettings } from "@/lib/stores"
|
||||||
import {
|
import {
|
||||||
cn,
|
cn,
|
||||||
copyToClipboard,
|
copyToClipboard,
|
||||||
@@ -25,24 +31,12 @@ import {
|
|||||||
getMeterState,
|
getMeterState,
|
||||||
parseSemVer,
|
parseSemVer,
|
||||||
} from "@/lib/utils"
|
} from "@/lib/utils"
|
||||||
import { EthernetIcon, GpuIcon, HourglassIcon, ThermometerIcon } from "../ui/icons"
|
import type { SystemRecord } from "@/types"
|
||||||
import { useStore } from "@nanostores/react"
|
|
||||||
import { $longestSystemNameLen, $userSettings } from "@/lib/stores"
|
|
||||||
import { Trans, useLingui } from "@lingui/react/macro"
|
|
||||||
import { useMemo, useRef, useState } from "react"
|
|
||||||
import { memo } from "react"
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuSeparator,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from "../ui/dropdown-menu"
|
|
||||||
import AlertButton from "../alerts/alert-button"
|
|
||||||
import { Dialog } from "../ui/dialog"
|
|
||||||
import { SystemDialog } from "../add-system"
|
import { SystemDialog } from "../add-system"
|
||||||
import { AlertDialog } from "../ui/alert-dialog"
|
import AlertButton from "../alerts/alert-button"
|
||||||
|
import { $router, Link } from "../router"
|
||||||
import {
|
import {
|
||||||
|
AlertDialog,
|
||||||
AlertDialogAction,
|
AlertDialogAction,
|
||||||
AlertDialogCancel,
|
AlertDialogCancel,
|
||||||
AlertDialogContent,
|
AlertDialogContent,
|
||||||
@@ -51,12 +45,16 @@ import {
|
|||||||
AlertDialogHeader,
|
AlertDialogHeader,
|
||||||
AlertDialogTitle,
|
AlertDialogTitle,
|
||||||
} from "../ui/alert-dialog"
|
} from "../ui/alert-dialog"
|
||||||
import { buttonVariants } from "../ui/button"
|
import { Button, buttonVariants } from "../ui/button"
|
||||||
import { t } from "@lingui/core/macro"
|
import { Dialog } from "../ui/dialog"
|
||||||
import { MeterState, SystemStatus } from "@/lib/enums"
|
import {
|
||||||
import { $router, Link } from "../router"
|
DropdownMenu,
|
||||||
import { getPagePath } from "@nanostores/router"
|
DropdownMenuContent,
|
||||||
import { isReadOnlyUser, pb } from "@/lib/api"
|
DropdownMenuItem,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "../ui/dropdown-menu"
|
||||||
|
import { EthernetIcon, GpuIcon, HourglassIcon, ThermometerIcon } from "../ui/icons"
|
||||||
|
|
||||||
const STATUS_COLORS = {
|
const STATUS_COLORS = {
|
||||||
[SystemStatus.Up]: "bg-green-500",
|
[SystemStatus.Up]: "bg-green-500",
|
||||||
@@ -290,7 +288,7 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "actions",
|
id: "actions",
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
name: () => t({ message: "Actions", comment: "Table column" }),
|
name: () => t({ message: "Actions", comment: "Table column" }),
|
||||||
size: 50,
|
size: 50,
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
@@ -305,7 +303,7 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
|
|||||||
|
|
||||||
function sortableHeader(context: HeaderContext<SystemRecord, unknown>) {
|
function sortableHeader(context: HeaderContext<SystemRecord, unknown>) {
|
||||||
const { column } = context
|
const { column } = context
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
const { Icon, hideSort, name }: { Icon: React.ElementType; name: () => string; hideSort: boolean } = column.columnDef
|
const { Icon, hideSort, name }: { Icon: React.ElementType; name: () => string; hideSort: boolean } = column.columnDef
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
@@ -353,7 +351,7 @@ export function IndicatorDot({ system, className }: { system: SystemRecord; clas
|
|||||||
export const ActionsButton = memo(({ system }: { system: SystemRecord }) => {
|
export const ActionsButton = memo(({ system }: { system: SystemRecord }) => {
|
||||||
const [deleteOpen, setDeleteOpen] = useState(false)
|
const [deleteOpen, setDeleteOpen] = useState(false)
|
||||||
const [editOpen, setEditOpen] = useState(false)
|
const [editOpen, setEditOpen] = useState(false)
|
||||||
let editOpened = useRef(false)
|
const editOpened = useRef(false)
|
||||||
const { t } = useLingui()
|
const { t } = useLingui()
|
||||||
const { id, status, host, name } = system
|
const { id, status, host, name } = system
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,31 @@
|
|||||||
|
import { Trans, useLingui } from "@lingui/react/macro"
|
||||||
|
import { useStore } from "@nanostores/react"
|
||||||
|
import { getPagePath } from "@nanostores/router"
|
||||||
import {
|
import {
|
||||||
ColumnDef,
|
type ColumnDef,
|
||||||
ColumnFiltersState,
|
type ColumnFiltersState,
|
||||||
getFilteredRowModel,
|
|
||||||
SortingState,
|
|
||||||
getSortedRowModel,
|
|
||||||
flexRender,
|
flexRender,
|
||||||
VisibilityState,
|
|
||||||
getCoreRowModel,
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
type Row,
|
||||||
|
type SortingState,
|
||||||
|
type Table as TableType,
|
||||||
useReactTable,
|
useReactTable,
|
||||||
Row,
|
type VisibilityState,
|
||||||
Table as TableType,
|
|
||||||
} from "@tanstack/react-table"
|
} from "@tanstack/react-table"
|
||||||
import { TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
|
import { useVirtualizer, type VirtualItem } from "@tanstack/react-virtual"
|
||||||
|
import {
|
||||||
|
ArrowDownIcon,
|
||||||
|
ArrowUpDownIcon,
|
||||||
|
ArrowUpIcon,
|
||||||
|
EyeIcon,
|
||||||
|
FilterIcon,
|
||||||
|
LayoutGridIcon,
|
||||||
|
LayoutListIcon,
|
||||||
|
Settings2Icon,
|
||||||
|
} from "lucide-react"
|
||||||
|
import { memo, useEffect, useMemo, useRef, useState } from "react"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
@@ -24,30 +38,16 @@ import {
|
|||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu"
|
} from "@/components/ui/dropdown-menu"
|
||||||
import { SystemRecord } from "@/types"
|
|
||||||
import {
|
|
||||||
ArrowUpDownIcon,
|
|
||||||
LayoutGridIcon,
|
|
||||||
LayoutListIcon,
|
|
||||||
ArrowDownIcon,
|
|
||||||
ArrowUpIcon,
|
|
||||||
Settings2Icon,
|
|
||||||
EyeIcon,
|
|
||||||
FilterIcon,
|
|
||||||
} from "lucide-react"
|
|
||||||
import { memo, useEffect, useMemo, useRef, useState } from "react"
|
|
||||||
import { $pausedSystems, $downSystems, $upSystems, $systems } from "@/lib/stores"
|
|
||||||
import { useStore } from "@nanostores/react"
|
|
||||||
import { cn, runOnce, useBrowserStorage } from "@/lib/utils"
|
|
||||||
import { $router, Link } from "../router"
|
|
||||||
import { useLingui, Trans } from "@lingui/react/macro"
|
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card"
|
|
||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/components/ui/input"
|
||||||
import { getPagePath } from "@nanostores/router"
|
import { TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
|
||||||
import SystemsTableColumns, { ActionsButton, IndicatorDot } from "./systems-table-columns"
|
|
||||||
import AlertButton from "../alerts/alert-button"
|
|
||||||
import { SystemStatus } from "@/lib/enums"
|
import { SystemStatus } from "@/lib/enums"
|
||||||
import { useVirtualizer, VirtualItem } from "@tanstack/react-virtual"
|
import { $downSystems, $pausedSystems, $systems, $upSystems } from "@/lib/stores"
|
||||||
|
import { cn, runOnce, useBrowserStorage } from "@/lib/utils"
|
||||||
|
import type { SystemRecord } from "@/types"
|
||||||
|
import AlertButton from "../alerts/alert-button"
|
||||||
|
import { $router, Link } from "../router"
|
||||||
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card"
|
||||||
|
import SystemsTableColumns, { ActionsButton, IndicatorDot } from "./systems-table-columns"
|
||||||
|
|
||||||
type ViewMode = "table" | "grid"
|
type ViewMode = "table" | "grid"
|
||||||
type StatusFilter = "all" | SystemRecord["status"]
|
type StatusFilter = "all" | SystemRecord["status"]
|
||||||
@@ -309,69 +309,63 @@ export default function SystemsTable() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const AllSystemsTable = memo(function ({
|
const AllSystemsTable = memo(
|
||||||
table,
|
({ table, rows, colLength }: { table: TableType<SystemRecord>; rows: Row<SystemRecord>[]; colLength: number }) => {
|
||||||
rows,
|
// The virtualizer will need a reference to the scrollable container element
|
||||||
colLength,
|
const scrollRef = useRef<HTMLDivElement>(null)
|
||||||
}: {
|
|
||||||
table: TableType<SystemRecord>
|
|
||||||
rows: Row<SystemRecord>[]
|
|
||||||
colLength: number
|
|
||||||
}) {
|
|
||||||
// The virtualizer will need a reference to the scrollable container element
|
|
||||||
const scrollRef = useRef<HTMLDivElement>(null)
|
|
||||||
|
|
||||||
const virtualizer = useVirtualizer<HTMLDivElement, HTMLTableRowElement>({
|
const virtualizer = useVirtualizer<HTMLDivElement, HTMLTableRowElement>({
|
||||||
count: rows.length,
|
count: rows.length,
|
||||||
estimateSize: () => (rows.length > 10 ? 56 : 60),
|
estimateSize: () => (rows.length > 10 ? 56 : 60),
|
||||||
getScrollElement: () => scrollRef.current,
|
getScrollElement: () => scrollRef.current,
|
||||||
overscan: 5,
|
overscan: 5,
|
||||||
})
|
})
|
||||||
const virtualRows = virtualizer.getVirtualItems()
|
const virtualRows = virtualizer.getVirtualItems()
|
||||||
|
|
||||||
const paddingTop = Math.max(0, virtualRows[0]?.start ?? 0 - virtualizer.options.scrollMargin)
|
const paddingTop = Math.max(0, virtualRows[0]?.start ?? 0 - virtualizer.options.scrollMargin)
|
||||||
const paddingBottom = Math.max(0, virtualizer.getTotalSize() - (virtualRows[virtualRows.length - 1]?.end ?? 0))
|
const paddingBottom = Math.max(0, virtualizer.getTotalSize() - (virtualRows[virtualRows.length - 1]?.end ?? 0))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"h-min max-h-[calc(100dvh-17rem)] max-w-full relative overflow-auto border rounded-md",
|
"h-min max-h-[calc(100dvh-17rem)] max-w-full relative overflow-auto border rounded-md",
|
||||||
// don't set min height if there are less than 2 rows, do set if we need to display the empty state
|
// don't set min height if there are less than 2 rows, do set if we need to display the empty state
|
||||||
(!rows.length || rows.length > 2) && "min-h-50"
|
(!rows.length || rows.length > 2) && "min-h-50"
|
||||||
)}
|
)}
|
||||||
ref={scrollRef}
|
ref={scrollRef}
|
||||||
>
|
>
|
||||||
{/* add header height to table size */}
|
{/* add header height to table size */}
|
||||||
<div style={{ height: `${virtualizer.getTotalSize() + 50}px`, paddingTop, paddingBottom }}>
|
<div style={{ height: `${virtualizer.getTotalSize() + 50}px`, paddingTop, paddingBottom }}>
|
||||||
<table className="text-sm w-full h-full">
|
<table className="text-sm w-full h-full">
|
||||||
<SystemsTableHead table={table} colLength={colLength} />
|
<SystemsTableHead table={table} colLength={colLength} />
|
||||||
<TableBody onMouseEnter={preloadSystemDetail}>
|
<TableBody onMouseEnter={preloadSystemDetail}>
|
||||||
{rows.length ? (
|
{rows.length ? (
|
||||||
virtualRows.map((virtualRow) => {
|
virtualRows.map((virtualRow) => {
|
||||||
const row = rows[virtualRow.index] as Row<SystemRecord>
|
const row = rows[virtualRow.index] as Row<SystemRecord>
|
||||||
return (
|
return (
|
||||||
<SystemTableRow
|
<SystemTableRow
|
||||||
key={row.id}
|
key={row.id}
|
||||||
row={row}
|
row={row}
|
||||||
virtualRow={virtualRow}
|
virtualRow={virtualRow}
|
||||||
length={rows.length}
|
length={rows.length}
|
||||||
colLength={colLength}
|
colLength={colLength}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
) : (
|
) : (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={colLength} className="h-37 text-center pointer-events-none">
|
<TableCell colSpan={colLength} className="h-37 text-center pointer-events-none">
|
||||||
<Trans>No systems found.</Trans>
|
<Trans>No systems found.</Trans>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
)}
|
)}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)
|
||||||
)
|
}
|
||||||
})
|
)
|
||||||
|
|
||||||
function SystemsTableHead({ table, colLength }: { table: TableType<SystemRecord>; colLength: number }) {
|
function SystemsTableHead({ table, colLength }: { table: TableType<SystemRecord>; colLength: number }) {
|
||||||
const { i18n } = useLingui()
|
const { i18n } = useLingui()
|
||||||
@@ -395,42 +389,44 @@ function SystemsTableHead({ table, colLength }: { table: TableType<SystemRecord>
|
|||||||
}, [i18n.locale, colLength])
|
}, [i18n.locale, colLength])
|
||||||
}
|
}
|
||||||
|
|
||||||
const SystemTableRow = memo(function ({
|
const SystemTableRow = memo(
|
||||||
row,
|
({
|
||||||
virtualRow,
|
row,
|
||||||
colLength,
|
virtualRow,
|
||||||
}: {
|
colLength,
|
||||||
row: Row<SystemRecord>
|
}: {
|
||||||
virtualRow: VirtualItem
|
row: Row<SystemRecord>
|
||||||
length: number
|
virtualRow: VirtualItem
|
||||||
colLength: number
|
length: number
|
||||||
}) {
|
colLength: number
|
||||||
const system = row.original
|
}) => {
|
||||||
const { t } = useLingui()
|
const system = row.original
|
||||||
return useMemo(() => {
|
const { t } = useLingui()
|
||||||
return (
|
return useMemo(() => {
|
||||||
<TableRow
|
return (
|
||||||
// data-state={row.getIsSelected() && "selected"}
|
<TableRow
|
||||||
className={cn("cursor-pointer transition-opacity relative safari:transform-3d", {
|
// data-state={row.getIsSelected() && "selected"}
|
||||||
"opacity-50": system.status === SystemStatus.Paused,
|
className={cn("cursor-pointer transition-opacity relative safari:transform-3d", {
|
||||||
})}
|
"opacity-50": system.status === SystemStatus.Paused,
|
||||||
>
|
})}
|
||||||
{row.getVisibleCells().map((cell) => (
|
>
|
||||||
<TableCell
|
{row.getVisibleCells().map((cell) => (
|
||||||
key={cell.id}
|
<TableCell
|
||||||
style={{
|
key={cell.id}
|
||||||
width: cell.column.getSize(),
|
style={{
|
||||||
height: virtualRow.size,
|
width: cell.column.getSize(),
|
||||||
}}
|
height: virtualRow.size,
|
||||||
className="py-0"
|
}}
|
||||||
>
|
className="py-0"
|
||||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
>
|
||||||
</TableCell>
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
))}
|
</TableCell>
|
||||||
</TableRow>
|
))}
|
||||||
)
|
</TableRow>
|
||||||
}, [system, system.status, colLength, t])
|
)
|
||||||
})
|
}, [system, system.status, colLength, t])
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const SystemCard = memo(
|
const SystemCard = memo(
|
||||||
({ row, table, colLength }: { row: Row<SystemRecord>; table: TableType<SystemRecord>; colLength: number }) => {
|
({ row, table, colLength }: { row: Row<SystemRecord>; table: TableType<SystemRecord>; colLength: number }) => {
|
||||||
@@ -471,7 +467,7 @@ const SystemCard = memo(
|
|||||||
if (!column.getIsVisible() || column.id === "system" || column.id === "actions") return null
|
if (!column.getIsVisible() || column.id === "system" || column.id === "actions") return null
|
||||||
const cell = row.getAllCells().find((cell) => cell.column.id === column.id)
|
const cell = row.getAllCells().find((cell) => cell.column.id === column.id)
|
||||||
if (!cell) return null
|
if (!cell) return null
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
const { Icon, name } = column.columnDef as ColumnDef<SystemRecord, unknown>
|
const { Icon, name } = column.columnDef as ColumnDef<SystemRecord, unknown>
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { t } from "@lingui/core/macro"
|
import { t } from "@lingui/core/macro"
|
||||||
import { type ClassValue, clsx } from "clsx"
|
import { type ClassValue, clsx } from "clsx"
|
||||||
import { timeDay, timeHour } from "d3-time"
|
import { timeDay, timeHour } from "d3-time"
|
||||||
|
import { listenKeys } from "nanostores"
|
||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
import { twMerge } from "tailwind-merge"
|
import { twMerge } from "tailwind-merge"
|
||||||
import { prependBasePath } from "@/components/router"
|
import { prependBasePath } from "@/components/router"
|
||||||
@@ -8,7 +9,6 @@ import { toast } from "@/components/ui/use-toast"
|
|||||||
import type { ChartTimeData, FingerprintRecord, SemVer, SystemRecord } from "@/types"
|
import type { ChartTimeData, FingerprintRecord, SemVer, SystemRecord } from "@/types"
|
||||||
import { HourFormat, MeterState, Unit } from "./enums"
|
import { HourFormat, MeterState, Unit } from "./enums"
|
||||||
import { $copyContent, $userSettings } from "./stores"
|
import { $copyContent, $userSettings } from "./stores"
|
||||||
import { listenKeys } from "nanostores"
|
|
||||||
|
|
||||||
export const FAVICON_DEFAULT = "favicon.svg"
|
export const FAVICON_DEFAULT = "favicon.svg"
|
||||||
export const FAVICON_GREEN = "favicon-green.svg"
|
export const FAVICON_GREEN = "favicon-green.svg"
|
||||||
|
|||||||
Reference in New Issue
Block a user