mirror of
https://github.com/henrygd/beszel.git
synced 2026-03-31 10:46:42 +02:00
Compare commits
8 Commits
encoding/j
...
b64318d9e8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b64318d9e8 | ||
|
|
0f5b1b5157 | ||
|
|
3c4ae46f50 | ||
|
|
c158b1aeeb | ||
|
|
684d92c497 | ||
|
|
bbd9595ec0 | ||
|
|
bbebb3e301 | ||
|
|
9d25181d1d |
Binary file not shown.
1517
beszel/site/package-lock.json
generated
1517
beszel/site/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -13,25 +13,25 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@henrygd/queue": "^1.0.7",
|
"@henrygd/queue": "^1.0.7",
|
||||||
"@henrygd/semaphore": "^0.0.2",
|
"@henrygd/semaphore": "^0.0.2",
|
||||||
"@lingui/detect-locale": "^5.3.3",
|
"@lingui/detect-locale": "^5.4.1",
|
||||||
"@lingui/macro": "^5.3.3",
|
"@lingui/macro": "^5.4.1",
|
||||||
"@lingui/react": "^5.3.3",
|
"@lingui/react": "^5.4.1",
|
||||||
"@nanostores/react": "^0.7.3",
|
"@nanostores/react": "^0.7.3",
|
||||||
"@nanostores/router": "^0.11.0",
|
"@nanostores/router": "^0.11.0",
|
||||||
"@radix-ui/react-alert-dialog": "^1.1.14",
|
"@radix-ui/react-alert-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-checkbox": "^1.3.2",
|
"@radix-ui/react-checkbox": "^1.3.3",
|
||||||
"@radix-ui/react-dialog": "^1.1.14",
|
"@radix-ui/react-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-direction": "^1.1.1",
|
"@radix-ui/react-direction": "^1.1.1",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.15",
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||||
"@radix-ui/react-label": "^2.1.7",
|
"@radix-ui/react-label": "^2.1.7",
|
||||||
"@radix-ui/react-select": "^2.2.5",
|
"@radix-ui/react-select": "^2.2.6",
|
||||||
"@radix-ui/react-separator": "^1.1.7",
|
"@radix-ui/react-separator": "^1.1.7",
|
||||||
"@radix-ui/react-slider": "^1.3.5",
|
"@radix-ui/react-slider": "^1.3.6",
|
||||||
"@radix-ui/react-slot": "^1.2.3",
|
"@radix-ui/react-slot": "^1.2.3",
|
||||||
"@radix-ui/react-switch": "^1.2.5",
|
"@radix-ui/react-switch": "^1.2.6",
|
||||||
"@radix-ui/react-tabs": "^1.1.12",
|
"@radix-ui/react-tabs": "^1.1.13",
|
||||||
"@radix-ui/react-toast": "^1.2.14",
|
"@radix-ui/react-toast": "^1.2.15",
|
||||||
"@radix-ui/react-tooltip": "^1.2.7",
|
"@radix-ui/react-tooltip": "^1.2.8",
|
||||||
"@tanstack/react-table": "^8.21.3",
|
"@tanstack/react-table": "^8.21.3",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
@@ -40,27 +40,27 @@
|
|||||||
"lucide-react": "^0.452.0",
|
"lucide-react": "^0.452.0",
|
||||||
"nanostores": "^0.11.4",
|
"nanostores": "^0.11.4",
|
||||||
"pocketbase": "^0.26.2",
|
"pocketbase": "^0.26.2",
|
||||||
"react": "^18.3.1",
|
"react": "^19.1.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^19.1.1",
|
||||||
"recharts": "^2.15.4",
|
"recharts": "^2.15.4",
|
||||||
"tailwind-merge": "^2.6.0",
|
"tailwind-merge": "^2.6.0",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
|
||||||
"valibot": "^0.42.1"
|
"valibot": "^0.42.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@lingui/cli": "^5.3.3",
|
"@lingui/cli": "^5.4.1",
|
||||||
"@lingui/swc-plugin": "^5.5.2",
|
"@lingui/swc-plugin": "^5.6.1",
|
||||||
"@lingui/vite-plugin": "^5.3.3",
|
"@lingui/vite-plugin": "^5.4.1",
|
||||||
"@tailwindcss/container-queries": "^0.1.1",
|
"@tailwindcss/container-queries": "^0.1.1",
|
||||||
"@types/bun": "^1.2.19",
|
"@tailwindcss/postcss": "^4.1.12",
|
||||||
"@types/react": "^18.3.23",
|
"@types/bun": "^1.2.20",
|
||||||
|
"@types/react": "^18.3.24",
|
||||||
"@types/react-dom": "^18.3.7",
|
"@types/react-dom": "^18.3.7",
|
||||||
"@vitejs/plugin-react-swc": "^3.11.0",
|
"@vitejs/plugin-react-swc": "^3.11.0",
|
||||||
"autoprefixer": "^10.4.21",
|
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
"tailwindcss": "^3.4.17",
|
"tailwindcss": "^4.1.12",
|
||||||
"tailwindcss-rtl": "^0.9.0",
|
"tailwindcss-rtl": "^0.9.0",
|
||||||
"typescript": "^5.8.3",
|
"tw-animate-css": "^1.3.7",
|
||||||
|
"typescript": "^5.9.2",
|
||||||
"vite": "^6.3.5"
|
"vite": "^6.3.5"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
export default {
|
export default {
|
||||||
plugins: {
|
plugins: {
|
||||||
tailwindcss: {},
|
"@tailwindcss/postcss": {},
|
||||||
autoprefixer: {},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import { ChevronDownIcon, ExternalLinkIcon, PlusIcon } from "lucide-react"
|
|||||||
import { memo, useEffect, useRef, useState } from "react"
|
import { memo, useEffect, useRef, useState } from "react"
|
||||||
import { $router, basePath, Link, navigate } from "./router"
|
import { $router, basePath, Link, navigate } from "./router"
|
||||||
import { SystemRecord } from "@/types"
|
import { SystemRecord } from "@/types"
|
||||||
|
import { SystemStatus } from "@/lib/enums"
|
||||||
import { AppleIcon, DockerIcon, TuxIcon, WindowsIcon } from "./ui/icons"
|
import { AppleIcon, DockerIcon, TuxIcon, WindowsIcon } from "./ui/icons"
|
||||||
import { InputCopy } from "./ui/input-copy"
|
import { InputCopy } from "./ui/input-copy"
|
||||||
import { getPagePath } from "@nanostores/router"
|
import { getPagePath } from "@nanostores/router"
|
||||||
@@ -105,7 +106,7 @@ export const SystemDialog = ({ setOpen, system }: { setOpen: (open: boolean) =>
|
|||||||
try {
|
try {
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
if (system) {
|
if (system) {
|
||||||
await pb.collection("systems").update(system.id, { ...data, status: "pending" })
|
await pb.collection("systems").update(system.id, { ...data, status: SystemStatus.Pending })
|
||||||
} else {
|
} else {
|
||||||
const createdSystem = await pb.collection("systems").create(data)
|
const createdSystem = await pb.collection("systems").create(data)
|
||||||
await pb.collection("fingerprints").create({
|
await pb.collection("fingerprints").create({
|
||||||
@@ -165,9 +166,7 @@ export const SystemDialog = ({ setOpen, system }: { setOpen: (open: boolean) =>
|
|||||||
<Trans>
|
<Trans>
|
||||||
Copy the installation command for the agent below, or register agents automatically with a{" "}
|
Copy the installation command for the agent below, or register agents automatically with a{" "}
|
||||||
<Link
|
<Link
|
||||||
onClick={() => {
|
onClick={() => setOpen(false)}
|
||||||
setOpen(false)
|
|
||||||
}}
|
|
||||||
href={getPagePath($router, "settings", { name: "tokens" })}
|
href={getPagePath($router, "settings", { name: "tokens" })}
|
||||||
className="link"
|
className="link"
|
||||||
>
|
>
|
||||||
@@ -274,7 +273,7 @@ interface CopyButtonProps {
|
|||||||
text: string
|
text: string
|
||||||
onClick: () => void
|
onClick: () => void
|
||||||
dropdownItems: DropdownItem[]
|
dropdownItems: DropdownItem[]
|
||||||
icon?: React.ReactElement
|
icon?: React.ReactElement<any>
|
||||||
}
|
}
|
||||||
|
|
||||||
const CopyButton = memo((props: CopyButtonProps) => {
|
const CopyButton = memo((props: CopyButtonProps) => {
|
||||||
|
|||||||
@@ -2,23 +2,22 @@ import { t } from "@lingui/core/macro"
|
|||||||
import { memo, useMemo, useState } from "react"
|
import { memo, useMemo, useState } from "react"
|
||||||
import { useStore } from "@nanostores/react"
|
import { useStore } from "@nanostores/react"
|
||||||
import { $alerts } from "@/lib/stores"
|
import { $alerts } from "@/lib/stores"
|
||||||
import { Dialog, DialogTrigger, DialogContent } from "@/components/ui/dialog"
|
|
||||||
import { BellIcon } from "lucide-react"
|
import { BellIcon } from "lucide-react"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { SystemRecord } from "@/types"
|
import { SystemRecord } from "@/types"
|
||||||
import { AlertDialogContent } from "./alerts-dialog"
|
import { AlertDialogContent } from "./alerts-sheet"
|
||||||
|
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/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)
|
||||||
const alerts = useStore($alerts)
|
const alerts = useStore($alerts)
|
||||||
|
|
||||||
const hasSystemAlert = alerts[system.id]?.size > 0
|
const hasSystemAlert = alerts[system.id]?.size > 0
|
||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() => (
|
() => (
|
||||||
<Dialog>
|
<Sheet>
|
||||||
<DialogTrigger asChild>
|
<SheetTrigger asChild>
|
||||||
<Button variant="ghost" size="icon" aria-label={t`Alerts`} data-nolink onClick={() => setOpened(true)}>
|
<Button variant="ghost" size="icon" aria-label={t`Alerts`} data-nolink onClick={() => setOpened(true)}>
|
||||||
<BellIcon
|
<BellIcon
|
||||||
className={cn("h-[1.2em] w-[1.2em] pointer-events-none", {
|
className={cn("h-[1.2em] w-[1.2em] pointer-events-none", {
|
||||||
@@ -26,32 +25,12 @@ export default memo(function AlertsButton({ system }: { system: SystemRecord })
|
|||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</SheetTrigger>
|
||||||
<DialogContent className="max-h-full sm:max-h-[95svh] overflow-auto max-w-[37rem]">
|
<SheetContent className="max-h-full overflow-auto w-145 !max-w-full p-4 sm:p-6">
|
||||||
{opened && <AlertDialogContent system={system} />}
|
{opened && <AlertDialogContent system={system} />}
|
||||||
</DialogContent>
|
</SheetContent>
|
||||||
</Dialog>
|
</Sheet>
|
||||||
),
|
),
|
||||||
[opened, hasSystemAlert]
|
[opened, hasSystemAlert]
|
||||||
)
|
)
|
||||||
|
|
||||||
// return useMemo(
|
|
||||||
// () => (
|
|
||||||
// <Sheet>
|
|
||||||
// <SheetTrigger asChild>
|
|
||||||
// <Button variant="ghost" size="icon" aria-label={t`Alerts`} data-nolink onClick={() => setOpened(true)}>
|
|
||||||
// <BellIcon
|
|
||||||
// className={cn("h-[1.2em] w-[1.2em] pointer-events-none", {
|
|
||||||
// "fill-primary": hasAlert,
|
|
||||||
// })}
|
|
||||||
// />
|
|
||||||
// </Button>
|
|
||||||
// </SheetTrigger>
|
|
||||||
// <SheetContent className="max-h-full overflow-auto w-[35em] p-4 sm:p-5">
|
|
||||||
// {opened && <AlertDialogContent system={system} />}
|
|
||||||
// </SheetContent>
|
|
||||||
// </Sheet>
|
|
||||||
// ),
|
|
||||||
// [opened, hasAlert]
|
|
||||||
// )
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ export function UserAuthForm({
|
|||||||
// }}
|
// }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<span className="translate-y-[1px]">{provider.displayName}</span>
|
<span className="translate-y-px">{provider.displayName}</span>
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -299,7 +299,7 @@ export function UserAuthForm({
|
|||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<button type="button" className={cn(buttonVariants({ variant: "outline" }))}>
|
<button type="button" className={cn(buttonVariants({ variant: "outline" }))}>
|
||||||
<img className="me-2 h-4 w-4 dark:invert" src={prependBasePath("/_/images/oauth2/github.svg")} alt="" />
|
<img className="me-2 h-4 w-4 dark:invert" src={prependBasePath("/_/images/oauth2/github.svg")} alt="" />
|
||||||
<span className="translate-y-[1px]">GitHub</span>
|
<span className="translate-y-px">GitHub</span>
|
||||||
</button>
|
</button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent style={{ maxWidth: 440, width: "90%" }}>
|
<DialogContent style={{ maxWidth: 440, width: "90%" }}>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Trans } from "@lingui/react/macro";
|
import { Trans } from "@lingui/react/macro"
|
||||||
import { useState, lazy, Suspense } from "react"
|
import { useState, lazy, Suspense } from "react"
|
||||||
import { Button, buttonVariants } from "@/components/ui/button"
|
import { Button, buttonVariants } from "@/components/ui/button"
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -35,18 +35,20 @@ export const navigate = (urlString: string) => {
|
|||||||
$router.open(urlString)
|
$router.open(urlString)
|
||||||
}
|
}
|
||||||
|
|
||||||
function onClick(e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) {
|
export function Link(props: React.AnchorHTMLAttributes<HTMLAnchorElement>) {
|
||||||
e.preventDefault()
|
return (
|
||||||
$router.open(new URL((e.currentTarget as HTMLAnchorElement).href).pathname)
|
<a
|
||||||
}
|
{...props}
|
||||||
|
onClick={(e) => {
|
||||||
export const Link = (props: React.AnchorHTMLAttributes<HTMLAnchorElement>) => {
|
e.preventDefault()
|
||||||
let clickFn = onClick
|
const href = props.href || ""
|
||||||
if (props.onClick) {
|
if (e.ctrlKey || e.metaKey) {
|
||||||
clickFn = (e) => {
|
window.open(href, "_blank")
|
||||||
onClick(e)
|
} else {
|
||||||
props.onClick?.(e)
|
navigate(href)
|
||||||
}
|
props.onClick?.(e)
|
||||||
}
|
}
|
||||||
return <a {...props} onClick={clickFn}></a>
|
}}
|
||||||
|
></a>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ const ActiveAlerts = () => {
|
|||||||
return (
|
return (
|
||||||
<Alert
|
<Alert
|
||||||
key={alert.id}
|
key={alert.id}
|
||||||
className="hover:-translate-y-[1px] duration-200 bg-transparent border-foreground/10 hover:shadow-md shadow-black"
|
className="hover:-translate-y-px duration-200 bg-transparent border-foreground/10 hover:shadow-md shadow-black"
|
||||||
>
|
>
|
||||||
<info.icon className="h-4 w-4" />
|
<info.icon className="h-4 w-4" />
|
||||||
<AlertTitle>
|
<AlertTitle>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
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 { isAdmin } from "@/lib/utils"
|
import { isAdmin } from "@/lib/utils"
|
||||||
import { Separator } from "@/components/ui/separator"
|
import { Separator } from "@/components/ui/separator"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ export default function SettingsProfilePage({ userSettings }: { userSettings: Us
|
|||||||
</div>
|
</div>
|
||||||
<Separator className="my-4" />
|
<Separator className="my-4" />
|
||||||
<form onSubmit={handleSubmit} className="space-y-5">
|
<form onSubmit={handleSubmit} className="space-y-5">
|
||||||
<div className="space-y-2">
|
<div className="grid gap-2">
|
||||||
<div className="mb-4">
|
<div className="mb-2">
|
||||||
<h3 className="mb-1 text-lg font-medium flex items-center gap-2">
|
<h3 className="mb-1 text-lg font-medium flex items-center gap-2">
|
||||||
<LanguagesIcon className="h-4 w-4" />
|
<LanguagesIcon className="h-4 w-4" />
|
||||||
<Trans>Language</Trans>
|
<Trans>Language</Trans>
|
||||||
@@ -73,8 +73,8 @@ export default function SettingsProfilePage({ userSettings }: { userSettings: Us
|
|||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<Separator />
|
<Separator />
|
||||||
<div className="space-y-2">
|
<div className="grid gap-2">
|
||||||
<div className="mb-4">
|
<div className="mb-2">
|
||||||
<h3 className="mb-1 text-lg font-medium">
|
<h3 className="mb-1 text-lg font-medium">
|
||||||
<Trans>Chart options</Trans>
|
<Trans>Chart options</Trans>
|
||||||
</h3>
|
</h3>
|
||||||
@@ -102,8 +102,8 @@ export default function SettingsProfilePage({ userSettings }: { userSettings: Us
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Separator />
|
<Separator />
|
||||||
<div className="space-y-2">
|
<div className="grid gap-2">
|
||||||
<div className="mb-4">
|
<div className="mb-2">
|
||||||
<h3 className="mb-1 text-lg font-medium">
|
<h3 className="mb-1 text-lg font-medium">
|
||||||
<Trans comment="Temperature / network units">Unit preferences</Trans>
|
<Trans comment="Temperature / network units">Unit preferences</Trans>
|
||||||
</h3>
|
</h3>
|
||||||
@@ -112,7 +112,7 @@ export default function SettingsProfilePage({ userSettings }: { userSettings: Us
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid sm:grid-cols-3 gap-4">
|
<div className="grid sm:grid-cols-3 gap-4">
|
||||||
<div className="space-y-2">
|
<div className="grid gap-2">
|
||||||
<Label className="block" htmlFor="unitTemp">
|
<Label className="block" htmlFor="unitTemp">
|
||||||
<Trans>Temperature unit</Trans>
|
<Trans>Temperature unit</Trans>
|
||||||
</Label>
|
</Label>
|
||||||
@@ -134,7 +134,7 @@ export default function SettingsProfilePage({ userSettings }: { userSettings: Us
|
|||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="grid gap-2">
|
||||||
<Label className="block" htmlFor="unitNet">
|
<Label className="block" htmlFor="unitNet">
|
||||||
<Trans comment="Context: Bytes or bits">Network unit</Trans>
|
<Trans comment="Context: Bytes or bits">Network unit</Trans>
|
||||||
</Label>
|
</Label>
|
||||||
@@ -156,7 +156,7 @@ export default function SettingsProfilePage({ userSettings }: { userSettings: Us
|
|||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="grid gap-2">
|
||||||
<Label className="block" htmlFor="unitDisk">
|
<Label className="block" htmlFor="unitDisk">
|
||||||
<Trans>Disk unit</Trans>
|
<Trans>Disk unit</Trans>
|
||||||
</Label>
|
</Label>
|
||||||
@@ -181,8 +181,8 @@ export default function SettingsProfilePage({ userSettings }: { userSettings: Us
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Separator />
|
<Separator />
|
||||||
<div className="space-y-2">
|
<div className="grid gap-2">
|
||||||
<div className="mb-4">
|
<div className="mb-2">
|
||||||
<h3 className="mb-1 text-lg font-medium">
|
<h3 className="mb-1 text-lg font-medium">
|
||||||
<Trans>Warning thresholds</Trans>
|
<Trans>Warning thresholds</Trans>
|
||||||
</h3>
|
</h3>
|
||||||
@@ -191,7 +191,7 @@ export default function SettingsProfilePage({ userSettings }: { userSettings: Us
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-2 lg:grid-cols-3 gap-4 items-end">
|
<div className="grid grid-cols-2 lg:grid-cols-3 gap-4 items-end">
|
||||||
<div className="space-y-1">
|
<div className="grid gap-2">
|
||||||
<Label htmlFor="colorWarn">
|
<Label htmlFor="colorWarn">
|
||||||
<Trans>Warning (%)</Trans>
|
<Trans>Warning (%)</Trans>
|
||||||
</Label>
|
</Label>
|
||||||
@@ -205,7 +205,7 @@ export default function SettingsProfilePage({ userSettings }: { userSettings: Us
|
|||||||
defaultValue={userSettings.colorWarn ?? 65}
|
defaultValue={userSettings.colorWarn ?? 65}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="grid gap-1">
|
||||||
<Label htmlFor="colorCrit">
|
<Label htmlFor="colorCrit">
|
||||||
<Trans>Critical (%)</Trans>
|
<Trans>Critical (%)</Trans>
|
||||||
</Label>
|
</Label>
|
||||||
|
|||||||
@@ -87,8 +87,8 @@ const SettingsNotificationsPage = ({ userSettings }: { userSettings: UserSetting
|
|||||||
</div>
|
</div>
|
||||||
<Separator className="my-4" />
|
<Separator className="my-4" />
|
||||||
<div className="space-y-5">
|
<div className="space-y-5">
|
||||||
<div className="space-y-2">
|
<div className="grid gap-2">
|
||||||
<div className="mb-4">
|
<div className="mb-2">
|
||||||
<h3 className="mb-1 text-lg font-medium">
|
<h3 className="mb-1 text-lg font-medium">
|
||||||
<Trans>Email notifications</Trans>
|
<Trans>Email notifications</Trans>
|
||||||
</h3>
|
</h3>
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import {
|
|||||||
$temperatureFilter,
|
$temperatureFilter,
|
||||||
} from "@/lib/stores"
|
} from "@/lib/stores"
|
||||||
import { ChartData, ChartTimes, ContainerStatsRecord, GPUData, SystemRecord, SystemStatsRecord } from "@/types"
|
import { ChartData, ChartTimes, ContainerStatsRecord, GPUData, SystemRecord, SystemStatsRecord } from "@/types"
|
||||||
import { ChartType, Unit, Os } from "@/lib/enums"
|
import { ChartType, Unit, Os, SystemStatus } from "@/lib/enums"
|
||||||
import React, { lazy, memo, useCallback, useEffect, useMemo, useRef, useState } from "react"
|
import React, { lazy, memo, useCallback, useEffect, useMemo, useRef, useState, type JSX } from "react"
|
||||||
import { Card, CardHeader, CardTitle, CardDescription } from "../ui/card"
|
import { Card, CardHeader, CardTitle, CardDescription } from "../ui/card"
|
||||||
import { useStore } from "@nanostores/react"
|
import { useStore } from "@nanostores/react"
|
||||||
import Spinner from "../spinner"
|
import Spinner from "../spinner"
|
||||||
@@ -382,9 +382,9 @@ export default function SystemDetail({ name }: { name: string }) {
|
|||||||
const hasGpuPowerData = lastGpuVals.some((gpu) => gpu.p !== undefined)
|
const hasGpuPowerData = lastGpuVals.some((gpu) => gpu.p !== undefined)
|
||||||
|
|
||||||
let translatedStatus: string = system.status
|
let translatedStatus: string = system.status
|
||||||
if (system.status === "up") {
|
if (system.status === SystemStatus.Up) {
|
||||||
translatedStatus = t({ message: "Up", comment: "Context: System is up" })
|
translatedStatus = t({ message: "Up", comment: "Context: System is up" })
|
||||||
} else if (system.status === "down") {
|
} else if (system.status === SystemStatus.Down) {
|
||||||
translatedStatus = t({ message: "Down", comment: "Context: System is down" })
|
translatedStatus = t({ message: "Down", comment: "Context: System is down" })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,7 +399,7 @@ export default function SystemDetail({ name }: { name: string }) {
|
|||||||
<div className="flex flex-wrap items-center gap-3 gap-y-2 text-sm opacity-90">
|
<div className="flex flex-wrap items-center gap-3 gap-y-2 text-sm opacity-90">
|
||||||
<div className="capitalize flex gap-2 items-center">
|
<div className="capitalize flex gap-2 items-center">
|
||||||
<span className={cn("relative flex h-3 w-3")}>
|
<span className={cn("relative flex h-3 w-3")}>
|
||||||
{system.status === "up" && (
|
{system.status === SystemStatus.Up && (
|
||||||
<span
|
<span
|
||||||
className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"
|
className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"
|
||||||
style={{ animationDuration: "1.5s" }}
|
style={{ animationDuration: "1.5s" }}
|
||||||
@@ -407,10 +407,10 @@ export default function SystemDetail({ name }: { name: string }) {
|
|||||||
)}
|
)}
|
||||||
<span
|
<span
|
||||||
className={cn("relative inline-flex rounded-full h-3 w-3", {
|
className={cn("relative inline-flex rounded-full h-3 w-3", {
|
||||||
"bg-green-500": system.status === "up",
|
"bg-green-500": system.status === SystemStatus.Up,
|
||||||
"bg-red-500": system.status === "down",
|
"bg-red-500": system.status === SystemStatus.Down,
|
||||||
"bg-primary/40": system.status === "paused",
|
"bg-primary/40": system.status === SystemStatus.Paused,
|
||||||
"bg-yellow-500": system.status === "pending",
|
"bg-yellow-500": system.status === SystemStatus.Pending,
|
||||||
})}
|
})}
|
||||||
></span>
|
></span>
|
||||||
</span>
|
</span>
|
||||||
@@ -872,10 +872,10 @@ function ChartCard({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className={cn("pb-2 sm:pb-4 odd:last-of-type:col-span-full", { "col-span-full": !grid })} ref={ref}>
|
<Card className={cn("pb-2 sm:pb-4 odd:last-of-type:col-span-full", { "col-span-full": !grid })} ref={ref}>
|
||||||
<CardHeader className="pb-5 pt-4 relative space-y-1 max-sm:py-3 max-sm:px-4">
|
<CardHeader className="pb-5 pt-4 gap-1 relative max-sm:py-3 max-sm:px-4">
|
||||||
<CardTitle className="text-xl sm:text-2xl">{title}</CardTitle>
|
<CardTitle className="text-xl sm:text-2xl">{title}</CardTitle>
|
||||||
<CardDescription>{description}</CardDescription>
|
<CardDescription>{description}</CardDescription>
|
||||||
{cornerEl && <div className="relative py-1 block sm:w-44 sm:absolute sm:top-2.5 sm:end-3.5">{cornerEl}</div>}
|
{cornerEl && <div className="relative py-1 block sm:w-44 sm:absolute sm:top-3.5 sm:end-3.5">{cornerEl}</div>}
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<div className="ps-0 w-[calc(100%-1.5em)] h-48 md:h-52 relative group">
|
<div className="ps-0 w-[calc(100%-1.5em)] h-48 md:h-52 relative group">
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -54,13 +54,15 @@ import {
|
|||||||
} from "../ui/alert-dialog"
|
} from "../ui/alert-dialog"
|
||||||
import { buttonVariants } from "../ui/button"
|
import { buttonVariants } from "../ui/button"
|
||||||
import { t } from "@lingui/core/macro"
|
import { t } from "@lingui/core/macro"
|
||||||
import { MeterState } from "@/lib/enums"
|
import { MeterState, SystemStatus } from "@/lib/enums"
|
||||||
|
import { $router, Link } from "../router"
|
||||||
|
import { getPagePath } from "@nanostores/router"
|
||||||
|
|
||||||
const STATUS_COLORS = {
|
const STATUS_COLORS = {
|
||||||
up: "bg-green-500",
|
[SystemStatus.Up]: "bg-green-500",
|
||||||
down: "bg-red-500",
|
[SystemStatus.Down]: "bg-red-500",
|
||||||
paused: "bg-primary/40",
|
[SystemStatus.Paused]: "bg-primary/40",
|
||||||
pending: "bg-yellow-500",
|
[SystemStatus.Pending]: "bg-yellow-500",
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -80,9 +82,9 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
|
|||||||
let filterInputLower = ""
|
let filterInputLower = ""
|
||||||
const nameCache = new Map<string, string>()
|
const nameCache = new Map<string, string>()
|
||||||
const statusTranslations = {
|
const statusTranslations = {
|
||||||
up: t`Up`.toLowerCase(),
|
[SystemStatus.Up]: t`Up`.toLowerCase(),
|
||||||
down: t`Down`.toLowerCase(),
|
[SystemStatus.Down]: t`Down`.toLowerCase(),
|
||||||
paused: t`Paused`.toLowerCase(),
|
[SystemStatus.Paused]: t`Paused`.toLowerCase(),
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
// match filter value against name or translated status
|
// match filter value against name or translated status
|
||||||
@@ -107,12 +109,22 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
|
|||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
invertSorting: false,
|
invertSorting: false,
|
||||||
Icon: ServerIcon,
|
Icon: ServerIcon,
|
||||||
cell: (info) => (
|
cell: (info) => {
|
||||||
<span className="flex gap-2 items-center font-medium text-sm text-nowrap md:ps-1 md:pe-5">
|
const { name } = info.row.original
|
||||||
<IndicatorDot system={info.row.original} />
|
return (
|
||||||
{info.getValue() as string}
|
<>
|
||||||
</span>
|
<span className="flex gap-2 items-center font-medium text-sm text-nowrap md:ps-1 md:pe-5">
|
||||||
),
|
<IndicatorDot system={info.row.original} />
|
||||||
|
{name}
|
||||||
|
</span>
|
||||||
|
<Link
|
||||||
|
href={getPagePath($router, "system", { name })}
|
||||||
|
className="inset-0 absolute size-full"
|
||||||
|
aria-label={name}
|
||||||
|
></Link>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
},
|
||||||
header: sortableHeader,
|
header: sortableHeader,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -174,7 +186,7 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
|
|||||||
}
|
}
|
||||||
|
|
||||||
const max = Math.max(...loadAverages)
|
const max = Math.max(...loadAverages)
|
||||||
if (max === 0 && (status === "paused" || minor < 12)) {
|
if (max === 0 && (status === SystemStatus.Paused || minor < 12)) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,10 +197,10 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
|
|||||||
<div className="flex items-center gap-[.35em] w-full tabular-nums tracking-tight">
|
<div className="flex items-center gap-[.35em] w-full tabular-nums tracking-tight">
|
||||||
<span
|
<span
|
||||||
className={cn("inline-block size-2 rounded-full me-0.5", {
|
className={cn("inline-block size-2 rounded-full me-0.5", {
|
||||||
[STATUS_COLORS.up]: threshold === MeterState.Good,
|
[STATUS_COLORS[SystemStatus.Up]]: threshold === MeterState.Good,
|
||||||
[STATUS_COLORS.pending]: threshold === MeterState.Warn,
|
[STATUS_COLORS[SystemStatus.Pending]]: threshold === MeterState.Warn,
|
||||||
[STATUS_COLORS.down]: threshold === MeterState.Crit,
|
[STATUS_COLORS[SystemStatus.Down]]: threshold === MeterState.Crit,
|
||||||
[STATUS_COLORS.paused]: status !== "up",
|
[STATUS_COLORS[SystemStatus.Paused]]: status !== SystemStatus.Up,
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
{loadAverages?.map((la, i) => (
|
{loadAverages?.map((la, i) => (
|
||||||
@@ -208,7 +220,7 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
|
|||||||
cell(info) {
|
cell(info) {
|
||||||
const sys = info.row.original
|
const sys = info.row.original
|
||||||
const userSettings = useStore($userSettings, { keys: ["unitNet"] })
|
const userSettings = useStore($userSettings, { keys: ["unitNet"] })
|
||||||
if (sys.status === "paused") {
|
if (sys.status === SystemStatus.Paused) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const { value, unit } = formatBytes(info.getValue() as number, true, userSettings.unitNet, false)
|
const { value, unit } = formatBytes(info.getValue() as number, true, userSettings.unitNet, false)
|
||||||
@@ -261,9 +273,9 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
|
|||||||
<IndicatorDot
|
<IndicatorDot
|
||||||
system={system}
|
system={system}
|
||||||
className={
|
className={
|
||||||
(system.status !== "up" && STATUS_COLORS.paused) ||
|
(system.status !== SystemStatus.Up && STATUS_COLORS[SystemStatus.Paused]) ||
|
||||||
(version === globalThis.BESZEL.HUB_VERSION && STATUS_COLORS.up) ||
|
(version === globalThis.BESZEL.HUB_VERSION && STATUS_COLORS[SystemStatus.Up]) ||
|
||||||
STATUS_COLORS.pending
|
STATUS_COLORS[SystemStatus.Pending]
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<span className="truncate max-w-14">{info.getValue() as string}</span>
|
<span className="truncate max-w-14">{info.getValue() as string}</span>
|
||||||
@@ -277,7 +289,7 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
|
|||||||
name: () => t({ message: "Actions", comment: "Table column" }),
|
name: () => t({ message: "Actions", comment: "Table column" }),
|
||||||
size: 50,
|
size: 50,
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<div className="flex justify-end items-center gap-1 -ms-3">
|
<div className="relative z-10 flex justify-end items-center gap-1 -ms-3">
|
||||||
<AlertButton system={row.original} />
|
<AlertButton system={row.original} />
|
||||||
<ActionsButton system={row.original} />
|
<ActionsButton system={row.original} />
|
||||||
</div>
|
</div>
|
||||||
@@ -313,7 +325,7 @@ function TableCellWithMeter(info: CellContext<SystemRecord, unknown>) {
|
|||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
"absolute inset-0 w-full h-full origin-left",
|
"absolute inset-0 w-full h-full origin-left",
|
||||||
(info.row.original.status !== "up" && STATUS_COLORS.paused) ||
|
(info.row.original.status !== SystemStatus.Up && STATUS_COLORS.paused) ||
|
||||||
(threshold === MeterState.Good && STATUS_COLORS.up) ||
|
(threshold === MeterState.Good && STATUS_COLORS.up) ||
|
||||||
(threshold === MeterState.Warn && STATUS_COLORS.pending) ||
|
(threshold === MeterState.Warn && STATUS_COLORS.pending) ||
|
||||||
STATUS_COLORS.down
|
STATUS_COLORS.down
|
||||||
@@ -331,7 +343,7 @@ export function IndicatorDot({ system, className }: { system: SystemRecord; clas
|
|||||||
className ||= STATUS_COLORS[system.status as keyof typeof STATUS_COLORS] || ""
|
className ||= STATUS_COLORS[system.status as keyof typeof STATUS_COLORS] || ""
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className={cn("flex-shrink-0 size-2 rounded-full", className)}
|
className={cn("shrink-0 size-2 rounded-full", className)}
|
||||||
// style={{ marginBottom: "-1px" }}
|
// style={{ marginBottom: "-1px" }}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@@ -349,7 +361,7 @@ export const ActionsButton = memo(({ system }: { system: SystemRecord }) => {
|
|||||||
<>
|
<>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button variant="ghost" size={"icon"} data-nolink>
|
<Button variant="ghost" size={"icon"}>
|
||||||
<span className="sr-only">
|
<span className="sr-only">
|
||||||
<Trans>Open menu</Trans>
|
<Trans>Open menu</Trans>
|
||||||
</span>
|
</span>
|
||||||
@@ -372,11 +384,11 @@ export const ActionsButton = memo(({ system }: { system: SystemRecord }) => {
|
|||||||
className={cn(isReadOnlyUser() && "hidden")}
|
className={cn(isReadOnlyUser() && "hidden")}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
pb.collection("systems").update(id, {
|
pb.collection("systems").update(id, {
|
||||||
status: status === "paused" ? "pending" : "paused",
|
status: status === SystemStatus.Paused ? SystemStatus.Pending : SystemStatus.Paused,
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{status === "paused" ? (
|
{status === SystemStatus.Paused ? (
|
||||||
<>
|
<>
|
||||||
<PlayCircleIcon className="me-2.5 size-4" />
|
<PlayCircleIcon className="me-2.5 size-4" />
|
||||||
<Trans>Resume</Trans>
|
<Trans>Resume</Trans>
|
||||||
|
|||||||
@@ -41,13 +41,14 @@ import { memo, useEffect, useMemo, useState } from "react"
|
|||||||
import { $systems } from "@/lib/stores"
|
import { $systems } from "@/lib/stores"
|
||||||
import { useStore } from "@nanostores/react"
|
import { useStore } from "@nanostores/react"
|
||||||
import { cn, useLocalStorage } from "@/lib/utils"
|
import { cn, useLocalStorage } from "@/lib/utils"
|
||||||
import { $router, Link, navigate } from "../router"
|
import { $router, Link } from "../router"
|
||||||
import { useLingui, Trans } from "@lingui/react/macro"
|
import { useLingui, Trans } from "@lingui/react/macro"
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card"
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card"
|
||||||
import { Input } from "../ui/input"
|
import { Input } from "../ui/input"
|
||||||
import { getPagePath } from "@nanostores/router"
|
import { getPagePath } from "@nanostores/router"
|
||||||
import SystemsTableColumns, { ActionsButton, IndicatorDot } from "./systems-table-columns"
|
import SystemsTableColumns, { ActionsButton, IndicatorDot } from "./systems-table-columns"
|
||||||
import AlertButton from "../alerts/alert-button"
|
import AlertButton from "../alerts/alert-button"
|
||||||
|
import { SystemStatus } from "@/lib/enums"
|
||||||
|
|
||||||
type ViewMode = "table" | "grid"
|
type ViewMode = "table" | "grid"
|
||||||
|
|
||||||
@@ -291,15 +292,9 @@ const SystemTableRow = memo(
|
|||||||
return (
|
return (
|
||||||
<TableRow
|
<TableRow
|
||||||
// data-state={row.getIsSelected() && "selected"}
|
// data-state={row.getIsSelected() && "selected"}
|
||||||
className={cn("cursor-pointer transition-opacity", {
|
className={cn("cursor-pointer transition-opacity relative", {
|
||||||
"opacity-50": system.status === "paused",
|
"opacity-50": system.status === SystemStatus.Paused,
|
||||||
})}
|
})}
|
||||||
onClick={(e) => {
|
|
||||||
const target = e.target as HTMLElement
|
|
||||||
if (!target.closest("[data-nolink]") && e.currentTarget.contains(target)) {
|
|
||||||
navigate(getPagePath($router, "system", { name: system.name }))
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{row.getVisibleCells().map((cell) => (
|
{row.getVisibleCells().map((cell) => (
|
||||||
<TableCell
|
<TableCell
|
||||||
@@ -307,7 +302,7 @@ const SystemTableRow = memo(
|
|||||||
style={{
|
style={{
|
||||||
width: cell.column.getSize(),
|
width: cell.column.getSize(),
|
||||||
}}
|
}}
|
||||||
className={cn("overflow-hidden relative", length > 10 ? "py-2" : "py-2.5")}
|
className={length > 10 ? "py-2" : "py-2.5"}
|
||||||
>
|
>
|
||||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
@@ -330,7 +325,7 @@ const SystemCard = memo(
|
|||||||
className={cn(
|
className={cn(
|
||||||
"cursor-pointer hover:shadow-md transition-all bg-transparent w-full dark:border-border duration-200 relative",
|
"cursor-pointer hover:shadow-md transition-all bg-transparent w-full dark:border-border duration-200 relative",
|
||||||
{
|
{
|
||||||
"opacity-50": system.status === "paused",
|
"opacity-50": system.status === SystemStatus.Paused,
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -345,14 +340,14 @@ const SystemCard = memo(
|
|||||||
</div>
|
</div>
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
{table.getColumn("actions")?.getIsVisible() && (
|
{table.getColumn("actions")?.getIsVisible() && (
|
||||||
<div className="flex gap-1 flex-shrink-0 relative z-10">
|
<div className="flex gap-1 shrink-0 relative z-10">
|
||||||
<AlertButton system={system} />
|
<AlertButton system={system} />
|
||||||
<ActionsButton system={system} />
|
<ActionsButton system={system} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-2.5 text-sm px-5 pt-3.5 pb-4">
|
<CardContent className="grid gap-2.5 text-sm px-5 pt-3.5 pb-4">
|
||||||
{table.getAllColumns().map((column) => {
|
{table.getAllColumns().map((column) => {
|
||||||
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)
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const AlertDialogContent = React.forwardRef<
|
|||||||
<AlertDialogPrimitive.Content
|
<AlertDialogPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-card p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-50% data-[state=closed]:slide-out-to-top-48% data-[state=open]:slide-in-from-left-50% data-[state=open]:slide-in-from-top-48% sm:rounded-lg",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -44,7 +44,7 @@ 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-start", className)} {...props} />
|
<div className={cn("grid gap-2 text-center sm:text-start", className)} {...props} />
|
||||||
)
|
)
|
||||||
AlertDialogHeader.displayName = "AlertDialogHeader"
|
AlertDialogHeader.displayName = "AlertDialogHeader"
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { cva, type VariantProps } from "class-variance-authority"
|
|||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
const badgeVariants = cva(
|
const badgeVariants = cva(
|
||||||
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority"
|
|||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
const buttonVariants = cva(
|
const buttonVariants = cva(
|
||||||
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 cursor-pointer",
|
||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
|
|||||||
@@ -5,16 +5,14 @@ import { cn } from "@/lib/utils"
|
|||||||
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
|
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn("rounded-lg border border-border/60 bg-card text-card-foreground shadow-sm", className)}
|
className={cn("rounded-lg border border-border/60 bg-card text-card-foreground shadow-xs", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
Card.displayName = "Card"
|
Card.displayName = "Card"
|
||||||
|
|
||||||
const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
||||||
({ className, ...props }, ref) => (
|
({ className, ...props }, ref) => <div ref={ref} className={cn("grid gap-1.5 p-6", className)} {...props} />
|
||||||
<div ref={ref} className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
CardHeader.displayName = "CardHeader"
|
CardHeader.displayName = "CardHeader"
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import * as RechartsPrimitive from "recharts"
|
|||||||
import { chartTimeData, cn } from "@/lib/utils"
|
import { chartTimeData, cn } from "@/lib/utils"
|
||||||
import { ChartData } from "@/types"
|
import { ChartData } from "@/types"
|
||||||
|
|
||||||
|
import type { JSX } from "react";
|
||||||
|
|
||||||
// Format: { THEME_NAME: CSS_SELECTOR }
|
// Format: { THEME_NAME: CSS_SELECTOR }
|
||||||
const THEMES = { light: "", dark: ".dark" } as const
|
const THEMES = { light: "", dark: ".dark" } as const
|
||||||
|
|
||||||
@@ -41,21 +43,21 @@ const ChartContainer = React.forwardRef<
|
|||||||
const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`
|
const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`
|
||||||
|
|
||||||
return (
|
return (
|
||||||
//<ChartContext.Provider value={{ config }}>
|
//<ChartContext.Provider value={{ config }}>
|
||||||
<div
|
//</ChartContext.Provider>
|
||||||
|
<div
|
||||||
data-chart={chartId}
|
data-chart={chartId}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-none [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line-line]:stroke-border [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none",
|
"text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line-line]:stroke-border [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-hidden [&_.recharts-surface]:outline-hidden",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{/* <ChartStyle id={chartId} config={config} /> */}
|
{/* <ChartStyle id={chartId} config={config} /> */}
|
||||||
<RechartsPrimitive.ResponsiveContainer>{children}</RechartsPrimitive.ResponsiveContainer>
|
<RechartsPrimitive.ResponsiveContainer>{children}</RechartsPrimitive.ResponsiveContainer>
|
||||||
</div>
|
</div>
|
||||||
//</ChartContext.Provider>
|
);
|
||||||
)
|
|
||||||
})
|
})
|
||||||
ChartContainer.displayName = "Chart"
|
ChartContainer.displayName = "Chart"
|
||||||
|
|
||||||
@@ -169,7 +171,7 @@ const ChartTooltipContent = React.forwardRef<
|
|||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"grid min-w-[7rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl",
|
"grid min-w-28 items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -196,7 +198,7 @@ const ChartTooltipContent = React.forwardRef<
|
|||||||
<itemConfig.icon />
|
<itemConfig.icon />
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
className={cn("shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]", {
|
className={cn("shrink-0 rounded-[2px] border-border bg-(--color-bg)", {
|
||||||
"h-2.5 w-2.5": indicator === "dot",
|
"h-2.5 w-2.5": indicator === "dot",
|
||||||
"w-1": indicator === "line",
|
"w-1": indicator === "line",
|
||||||
"w-0 border-[1.5px] border-dashed bg-transparent": indicator === "dashed",
|
"w-0 border-[1.5px] border-dashed bg-transparent": indicator === "dashed",
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const Checkbox = React.forwardRef<
|
|||||||
<CheckboxPrimitive.Root
|
<CheckboxPrimitive.Root
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"peer size-4 flex items-center justify-center shrink-0 rounded-[.3em] border border-input ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
|
"peer size-4 flex items-center justify-center shrink-0 rounded-[.3em] border border-input ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
|
||||||
|
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const Command = React.forwardRef<
|
|||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<CommandPrimitive
|
<CommandPrimitive
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn("flex h-full w-full flex-col overflow-hidden bg-popover text-popover-foreground", className)}
|
className={cn("flex h-full w-full flex-col overflow-hidden bg-card", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
@@ -44,7 +44,7 @@ const CommandInput = React.forwardRef<
|
|||||||
<CommandPrimitive.Input
|
<CommandPrimitive.Input
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
|
"flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-hidden placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -105,7 +105,7 @@ const CommandItem = React.forwardRef<
|
|||||||
<CommandPrimitive.Item
|
<CommandPrimitive.Item
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative flex cursor-default opacity-70 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent/70 aria-selected:opacity-90 data-[disabled='true']:pointer-events-none data-[disabled='true']:opacity-50",
|
"relative flex cursor-default opacity-70 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-hidden aria-selected:bg-accent/60 aria-selected:opacity-90 data-[disabled='true']:pointer-events-none data-[disabled='true']:opacity-50",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -36,13 +36,13 @@ const DialogContent = React.forwardRef<
|
|||||||
<DialogPrimitive.Content
|
<DialogPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-card p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-card p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-50% data-[state=closed]:slide-out-to-top-48% data-[state=open]:slide-in-from-left-50% data-[state=open]:slide-in-from-top-48% sm:rounded-lg",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
<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">
|
<DialogPrimitive.Close className="absolute end-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden 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,7 +52,7 @@ 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-start", className)} {...props} />
|
<div className={cn("grid gap-1.5 text-center sm:text-start", className)} {...props} />
|
||||||
)
|
)
|
||||||
DialogHeader.displayName = "DialogHeader"
|
DialogHeader.displayName = "DialogHeader"
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const DropdownMenuSubTrigger = React.forwardRef<
|
|||||||
<DropdownMenuPrimitive.SubTrigger
|
<DropdownMenuPrimitive.SubTrigger
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex cursor-default select-none items-center rounded-sm px-2.5 py-1.5 text-sm outline-none focus:bg-accent/70 data-[state=open]:bg-accent/70",
|
"flex select-none items-center rounded-sm px-2.5 py-1.5 text-sm outline-hidden focus:bg-accent/70 data-[state=open]:bg-accent/70",
|
||||||
inset && "ps-8",
|
inset && "ps-8",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
@@ -44,7 +44,7 @@ const DropdownMenuSubContent = React.forwardRef<
|
|||||||
<DropdownMenuPrimitive.SubContent
|
<DropdownMenuPrimitive.SubContent
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
"z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -61,7 +61,7 @@ const DropdownMenuContent = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
sideOffset={sideOffset}
|
sideOffset={sideOffset}
|
||||||
className={cn(
|
className={cn(
|
||||||
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
"z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -79,7 +79,7 @@ const DropdownMenuItem = React.forwardRef<
|
|||||||
<DropdownMenuPrimitive.Item
|
<DropdownMenuPrimitive.Item
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative flex cursor-default select-none items-center rounded-sm px-2.5 py-1.5 text-sm outline-none focus:bg-accent/70 focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
"relative flex select-none items-center rounded-sm px-2.5 py-1.5 text-sm outline-hidden focus:bg-accent/70 focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
|
||||||
inset && "ps-8",
|
inset && "ps-8",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
@@ -95,7 +95,7 @@ const DropdownMenuCheckboxItem = React.forwardRef<
|
|||||||
<DropdownMenuPrimitive.CheckboxItem
|
<DropdownMenuPrimitive.CheckboxItem
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 ps-8 pe-2 text-sm outline-none focus:bg-accent/70 focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
"relative flex select-none items-center rounded-sm py-1.5 ps-8 pe-2 text-sm outline-hidden focus:bg-accent/70 focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
checked={checked}
|
checked={checked}
|
||||||
@@ -118,7 +118,7 @@ const DropdownMenuRadioItem = React.forwardRef<
|
|||||||
<DropdownMenuPrimitive.RadioItem
|
<DropdownMenuPrimitive.RadioItem
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 ps-8 pe-2 text-sm outline-none focus:bg-accent/70 focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
"relative flex cursor-default select-none items-center rounded-sm py-1.5 ps-8 pe-2 text-sm outline-hidden focus:bg-accent/70 focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export function InputCopy({ value, id, name }: { value: string; id: string; name
|
|||||||
<Input readOnly id={id} name={name} value={value} required></Input>
|
<Input readOnly id={id} name={name} value={value} required></Input>
|
||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
"h-6 w-24 bg-gradient-to-r rtl:bg-gradient-to-l from-transparent to-background to-65% absolute top-2 end-1 pointer-events-none"
|
"h-6 w-24 bg-linear-to-r rtl:bg-linear-to-l from-transparent to-background to-65% absolute top-2 end-1 pointer-events-none"
|
||||||
}
|
}
|
||||||
></div>
|
></div>
|
||||||
<TooltipProvider delayDuration={100} disableHoverableContent>
|
<TooltipProvider delayDuration={100} disableHoverableContent>
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ const InputTags = React.forwardRef<HTMLInputElement, InputTagsProps>(
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"bg-background min-h-10 flex w-full flex-wrap gap-2 rounded-md border px-3 py-2 text-sm placeholder:text-muted-foreground has-[:focus-visible]:outline-none ring-offset-background has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-ring has-[:focus-visible]:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
"bg-background min-h-10 flex w-full flex-wrap gap-2 rounded-md border px-3 py-2 text-sm placeholder:text-muted-foreground has-focus-visible:outline-hidden ring-offset-background has-focus-visible:ring-2 has-focus-visible:ring-ring has-focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -53,7 +53,7 @@ const InputTags = React.forwardRef<HTMLInputElement, InputTagsProps>(
|
|||||||
</Badge>
|
</Badge>
|
||||||
))}
|
))}
|
||||||
<input
|
<input
|
||||||
className="flex-1 outline-none bg-background placeholder:text-muted-foreground"
|
className="flex-1 outline-hidden bg-background placeholder:text-muted-foreground"
|
||||||
value={pendingDataPoint}
|
value={pendingDataPoint}
|
||||||
onChange={(e) => setPendingDataPoint(e.target.value)}
|
onChange={(e) => setPendingDataPoint(e.target.value)}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(({ className, type,
|
|||||||
<input
|
<input
|
||||||
type={type}
|
type={type}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-10 w-full rounded-md border bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
"flex h-10 w-full rounded-md border bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const SelectTrigger = React.forwardRef<
|
|||||||
<SelectPrimitive.Trigger
|
<SelectPrimitive.Trigger
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-10 w-full items-center justify-between rounded-md border bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
"flex h-10 w-full items-center justify-between rounded-md border bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -66,7 +66,7 @@ const SelectContent = React.forwardRef<
|
|||||||
<SelectPrimitive.Content
|
<SelectPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
"relative z-50 max-h-96 min-w-32 overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||||
position === "popper" &&
|
position === "popper" &&
|
||||||
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
||||||
className
|
className
|
||||||
@@ -79,7 +79,7 @@ const SelectContent = React.forwardRef<
|
|||||||
className={cn(
|
className={cn(
|
||||||
"p-1",
|
"p-1",
|
||||||
position === "popper" &&
|
position === "popper" &&
|
||||||
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
|
"h-(--radix-select-trigger-height) w-full min-w-(--radix-select-trigger-width)"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
@@ -105,7 +105,7 @@ const SelectItem = React.forwardRef<
|
|||||||
<SelectPrimitive.Item
|
<SelectPrimitive.Item
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 ps-8 pe-2 text-sm outline-none focus:bg-accent/70 focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 ps-8 pe-2 text-sm outline-hidden focus:bg-accent/70 focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const Separator = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
decorative={decorative}
|
decorative={decorative}
|
||||||
orientation={orientation}
|
orientation={orientation}
|
||||||
className={cn("shrink-0 bg-border", orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]", className)}
|
className={cn("shrink-0 bg-border", orientation === "horizontal" ? "h-px w-full" : "h-full w-px", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
|
|||||||
101
beszel/site/src/components/ui/sheet.tsx
Normal file
101
beszel/site/src/components/ui/sheet.tsx
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import * as SheetPrimitive from "@radix-ui/react-dialog"
|
||||||
|
import { XIcon } from "lucide-react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
|
||||||
|
return <SheetPrimitive.Root data-slot="sheet" {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function SheetTrigger({ ...props }: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
|
||||||
|
return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function SheetClose({ ...props }: React.ComponentProps<typeof SheetPrimitive.Close>) {
|
||||||
|
return <SheetPrimitive.Close data-slot="sheet-close" {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function SheetPortal({ ...props }: React.ComponentProps<typeof SheetPrimitive.Portal>) {
|
||||||
|
return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function SheetOverlay({ className, ...props }: React.ComponentProps<typeof SheetPrimitive.Overlay>) {
|
||||||
|
return (
|
||||||
|
<SheetPrimitive.Overlay
|
||||||
|
data-slot="sheet-overlay"
|
||||||
|
className={cn(
|
||||||
|
"data-[state=open]:animate-in duration-500 isolate data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/40",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function SheetContent({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
side = "right",
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof SheetPrimitive.Content> & {
|
||||||
|
side?: "top" | "right" | "bottom" | "left"
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<SheetPortal>
|
||||||
|
<SheetOverlay />
|
||||||
|
<SheetPrimitive.Content
|
||||||
|
data-slot="sheet-content"
|
||||||
|
className={cn(
|
||||||
|
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-[400ms]",
|
||||||
|
side === "right" &&
|
||||||
|
"data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
|
||||||
|
side === "left" &&
|
||||||
|
"data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
|
||||||
|
side === "top" &&
|
||||||
|
"data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",
|
||||||
|
side === "bottom" &&
|
||||||
|
"data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
<SheetPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
|
||||||
|
<XIcon className="size-4" />
|
||||||
|
<span className="sr-only">Close</span>
|
||||||
|
</SheetPrimitive.Close>
|
||||||
|
</SheetPrimitive.Content>
|
||||||
|
</SheetPortal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return <div data-slot="sheet-header" className={cn("flex flex-col gap-1.5 p-4", className)} {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return <div data-slot="sheet-footer" className={cn("mt-auto flex flex-col gap-2 p-4", className)} {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function SheetTitle({ className, ...props }: React.ComponentProps<typeof SheetPrimitive.Title>) {
|
||||||
|
return (
|
||||||
|
<SheetPrimitive.Title
|
||||||
|
data-slot="sheet-title"
|
||||||
|
className={cn("text-foreground font-semibold", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function SheetDescription({ className, ...props }: React.ComponentProps<typeof SheetPrimitive.Description>) {
|
||||||
|
return (
|
||||||
|
<SheetPrimitive.Description
|
||||||
|
data-slot="sheet-description"
|
||||||
|
className={cn("text-muted-foreground text-sm", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Sheet, SheetTrigger, SheetClose, SheetContent, SheetHeader, SheetFooter, SheetTitle, SheetDescription }
|
||||||
@@ -15,7 +15,7 @@ const Slider = React.forwardRef<
|
|||||||
<SliderPrimitive.Track className="relative h-2 w-full grow overflow-hidden rounded-full bg-secondary">
|
<SliderPrimitive.Track className="relative h-2 w-full grow overflow-hidden rounded-full bg-secondary">
|
||||||
<SliderPrimitive.Range className="absolute h-full bg-primary" />
|
<SliderPrimitive.Range className="absolute h-full bg-primary" />
|
||||||
</SliderPrimitive.Track>
|
</SliderPrimitive.Track>
|
||||||
<SliderPrimitive.Thumb className="block h-5 w-5 rounded-full border-2 border-primary bg-background ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50" />
|
<SliderPrimitive.Thumb className="block h-5 w-5 rounded-full border-2 border-primary bg-background ring-offset-background transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50" />
|
||||||
</SliderPrimitive.Root>
|
</SliderPrimitive.Root>
|
||||||
))
|
))
|
||||||
Slider.displayName = SliderPrimitive.Root.displayName
|
Slider.displayName = SliderPrimitive.Root.displayName
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const Switch = React.forwardRef<
|
|||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<SwitchPrimitives.Root
|
<SwitchPrimitives.Root
|
||||||
className={cn(
|
className={cn(
|
||||||
"peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
|
"peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -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 rtl: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 data-[state=checked]:rtl:-translate-x-5 data-[state=unchecked]:translate-x-0"
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</SwitchPrimitives.Root>
|
</SwitchPrimitives.Root>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ TableBody.displayName = "TableBody"
|
|||||||
|
|
||||||
const TableFooter = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
|
const TableFooter = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
|
||||||
({ className, ...props }, ref) => (
|
({ className, ...props }, ref) => (
|
||||||
<tfoot ref={ref} className={cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", className)} {...props} />
|
<tfoot ref={ref} className={cn("border-t bg-muted/50 font-medium last:[&>tr]:border-b-0", className)} {...props} />
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
TableFooter.displayName = "TableFooter"
|
TableFooter.displayName = "TableFooter"
|
||||||
@@ -37,7 +37,7 @@ const TableRow = React.forwardRef<HTMLTableRowElement, React.HTMLAttributes<HTML
|
|||||||
<tr
|
<tr
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"border-b border-border/60 hover:bg-muted/40 dark:hover:bg-muted/20 data-[state=selected]:!bg-muted",
|
"border-b border-border/60 hover:bg-muted/40 dark:hover:bg-muted/20 data-[state=selected]:bg-muted!",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ const TabsTrigger = React.forwardRef<
|
|||||||
<TabsPrimitive.Trigger
|
<TabsPrimitive.Trigger
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
|
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-xs cursor-pointer",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -42,7 +42,7 @@ const TabsContent = React.forwardRef<
|
|||||||
<TabsPrimitive.Content
|
<TabsPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
"mt-2 ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(({ classNa
|
|||||||
return (
|
return (
|
||||||
<textarea
|
<textarea
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex min-h-14 w-full rounded-md border bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
"flex min-h-14 w-full rounded-md border bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const ToastViewport = React.forwardRef<
|
|||||||
<ToastPrimitives.Viewport
|
<ToastPrimitives.Viewport
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed top-0 z-[100] flex max-h-dvh w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
|
"fixed top-0 z-100 flex max-h-dvh w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -23,7 +23,7 @@ const ToastViewport = React.forwardRef<
|
|||||||
ToastViewport.displayName = ToastPrimitives.Viewport.displayName
|
ToastViewport.displayName = ToastPrimitives.Viewport.displayName
|
||||||
|
|
||||||
const toastVariants = cva(
|
const toastVariants = cva(
|
||||||
"group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pe-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
|
"group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pe-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-(--radix-toast-swipe-end-x) data-[swipe=move]:translate-x-(--radix-toast-swipe-move-x) data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full sm:data-[state=open]:slide-in-from-bottom-full",
|
||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
@@ -52,7 +52,7 @@ const ToastAction = React.forwardRef<
|
|||||||
<ToastPrimitives.Action
|
<ToastPrimitives.Action
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
|
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 hover:group-[.destructive]:border-destructive/30 hover:group-[.destructive]:bg-destructive hover:group-[.destructive]:text-destructive-foreground focus:group-[.destructive]:ring-destructive",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -67,7 +67,7 @@ const ToastClose = React.forwardRef<
|
|||||||
<ToastPrimitives.Close
|
<ToastPrimitives.Close
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
|
"absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-hidden focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 hover:group-[.destructive]:text-red-50 focus:group-[.destructive]:ring-red-400 focus:group-[.destructive]:ring-offset-red-600",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
toast-close=""
|
toast-close=""
|
||||||
|
|||||||
@@ -1,6 +1,19 @@
|
|||||||
@tailwind base;
|
@import "tailwindcss";
|
||||||
@tailwind components;
|
@import "tw-animate-css";
|
||||||
@tailwind utilities;
|
|
||||||
|
@config '../tailwind.config.js';
|
||||||
|
|
||||||
|
@utility link {
|
||||||
|
@apply text-primary font-medium underline-offset-4 hover:underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
@utility ns-dialog {
|
||||||
|
/* New system dialog width */
|
||||||
|
min-width: 30.3rem;
|
||||||
|
:where(:lang(zh), :lang(zh-CN), :lang(ko)) & {
|
||||||
|
min-width: 27.9rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
:root {
|
:root {
|
||||||
@@ -57,18 +70,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fonts */
|
@layer utilities {
|
||||||
@supports (font-variation-settings: normal) {
|
/* Fonts */
|
||||||
:root {
|
@supports (font-variation-settings: normal) {
|
||||||
font-family: Inter, InterVariable, sans-serif;
|
:root {
|
||||||
|
font-family: Inter, InterVariable, sans-serif;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: InterVariable;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 100 900;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("/static/InterVariable.woff2?v=4.0") format("woff2");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
@font-face {
|
|
||||||
font-family: InterVariable;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 100 900;
|
|
||||||
font-display: swap;
|
|
||||||
src: url("/static/InterVariable.woff2?v=4.0") format("woff2");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
@@ -79,18 +94,8 @@
|
|||||||
body {
|
body {
|
||||||
@apply bg-background text-foreground;
|
@apply bg-background text-foreground;
|
||||||
}
|
}
|
||||||
}
|
button {
|
||||||
|
cursor: pointer;
|
||||||
@layer utilities {
|
|
||||||
.link {
|
|
||||||
@apply text-primary font-medium underline-offset-4 hover:underline;
|
|
||||||
}
|
|
||||||
/* New system dialog width */
|
|
||||||
.ns-dialog {
|
|
||||||
min-width: 30.3rem;
|
|
||||||
}
|
|
||||||
:where(:lang(zh), :lang(zh-CN), :lang(ko)) .ns-dialog {
|
|
||||||
min-width: 27.9rem;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,3 +28,11 @@ export enum MeterState {
|
|||||||
Warn,
|
Warn,
|
||||||
Crit,
|
Crit,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** System status states */
|
||||||
|
export enum SystemStatus {
|
||||||
|
Up = "up",
|
||||||
|
Down = "down",
|
||||||
|
Pending = "pending",
|
||||||
|
Paused = "paused",
|
||||||
|
}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ export function useIntersectionObserver({
|
|||||||
entry: undefined,
|
entry: undefined,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const callbackRef = useRef<UseIntersectionObserverOptions["onChange"]>()
|
const callbackRef = useRef<UseIntersectionObserverOptions["onChange"]>(undefined)
|
||||||
|
|
||||||
callbackRef.current = onChange
|
callbackRef.current = onChange
|
||||||
|
|
||||||
|
|||||||
@@ -442,7 +442,13 @@ export const alertInfo: Record<string, AlertInfo> = {
|
|||||||
export const getHostDisplayValue = (system: SystemRecord): string => system.host.slice(system.host.lastIndexOf("/") + 1)
|
export const getHostDisplayValue = (system: SystemRecord): string => system.host.slice(system.host.lastIndexOf("/") + 1)
|
||||||
|
|
||||||
/** Generate a random token for the agent */
|
/** Generate a random token for the agent */
|
||||||
export const generateToken = () => crypto?.randomUUID() ?? (performance.now() * Math.random()).toString(16)
|
export const generateToken = () => {
|
||||||
|
try {
|
||||||
|
return crypto?.randomUUID()
|
||||||
|
} catch (e) {
|
||||||
|
return Array.from({ length: 2 }, () => (performance.now() * Math.random()).toString(16).replace(".", "-")).join("-")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Get the hub URL from the global BESZEL object */
|
/** Get the hub URL from the global BESZEL object */
|
||||||
export const getHubURL = () => BESZEL?.HUB_URL || window.location.origin
|
export const getHubURL = () => BESZEL?.HUB_URL || window.location.origin
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import Navbar from "./components/navbar.tsx"
|
|||||||
import { I18nProvider } from "@lingui/react"
|
import { I18nProvider } from "@lingui/react"
|
||||||
import { i18n } from "@lingui/core"
|
import { i18n } from "@lingui/core"
|
||||||
import { getLocale, dynamicActivate } from "./lib/i18n.ts"
|
import { getLocale, dynamicActivate } from "./lib/i18n.ts"
|
||||||
|
import { SystemStatus } from "./lib/enums.ts"
|
||||||
|
|
||||||
// const ServerDetail = lazy(() => import('./components/routes/system.tsx'))
|
// const ServerDetail = lazy(() => import('./components/routes/system.tsx'))
|
||||||
const LoginPage = lazy(() => import("./components/login/login.tsx"))
|
const LoginPage = lazy(() => import("./components/login/login.tsx"))
|
||||||
@@ -50,10 +51,10 @@ const App = memo(() => {
|
|||||||
} else {
|
} else {
|
||||||
let up = false
|
let up = false
|
||||||
for (const system of systems) {
|
for (const system of systems) {
|
||||||
if (system.status === "down") {
|
if (system.status === SystemStatus.Down) {
|
||||||
updateFavicon("favicon-red.svg")
|
updateFavicon("favicon-red.svg")
|
||||||
return
|
return
|
||||||
} else if (system.status === "up") {
|
} else if (system.status === SystemStatus.Up) {
|
||||||
up = true
|
up = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,11 +94,11 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
require("@tailwindcss/container-queries"),
|
// require("@tailwindcss/container-queries"),
|
||||||
require("tailwindcss-animate"),
|
// require("tailwindcss-animate"),
|
||||||
require("tailwindcss-rtl"),
|
// require("tailwindcss-rtl"),
|
||||||
function ({ addVariant }) {
|
// function ({ addVariant }) {
|
||||||
addVariant("light", ".light &")
|
// addVariant("light", ".light &")
|
||||||
},
|
// },
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user