import { t } from "@lingui/core/macro" import { Trans, Plural } 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 { getPagePath } from "@nanostores/router" import { Checkbox } from "@/components/ui/checkbox" import { DialogTitle, DialogDescription } from "@/components/ui/dialog" import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs" import { ServerIcon, GlobeIcon } from "lucide-react" import { $router, Link } from "@/components/router" import { DialogHeader } from "@/components/ui/dialog" import { pb } from "@/lib/api" const Slider = lazy(() => import("@/components/ui/slider")) const endpoint = "/api/beszel/user-alerts" const alertDebounce = 100 const alertKeys = Object.keys(alertInfo) as (keyof typeof alertInfo)[] const failedUpdateToast = (error: unknown) => { console.error(error) toast({ title: t`Failed to update alert`, description: t`Please check logs for more details.`, variant: "destructive", }) } /** Create or update alerts for a given name and systems */ const upsertAlerts = debounce( async ({ name, value, min, systems }: { name: string; value: number; min: number; systems: string[] }) => { try { await pb.send<{ success: boolean }>(endpoint, { method: "POST", // overwrite is always true because we've done filtering client side body: { name, value, min, systems, overwrite: true }, }) } catch (error) { failedUpdateToast(error) } }, alertDebounce ) /** Delete alerts for a given name and systems */ const deleteAlerts = debounce(async ({ name, systems }: { name: string; systems: string[] }) => { try { await pb.send<{ success: boolean }>(endpoint, { method: "DELETE", body: { name, systems }, }) } catch (error) { failedUpdateToast(error) } }, alertDebounce) export const AlertDialogContent = memo(function AlertDialogContent({ system }: { system: SystemRecord }) { const alerts = useStore($alerts) const [overwriteExisting, setOverwriteExisting] = useState(false) const [currentTab, setCurrentTab] = useState("system") const systemAlerts = alerts[system.id] ?? new Map() // We need to keep a copy of alerts when we switch to global tab. If we always compare to // current alerts, it will only be updated when first checked, then won't be updated because // after that it exists. const alertsWhenGlobalSelected = useMemo(() => { return currentTab === "global" ? structuredClone(alerts) : alerts }, [currentTab]) return ( <> Alerts See{" "} notification settings {" "} to configure how you receive alerts. {system.name} All Systems
{alertKeys.map((name) => ( ))}
{alertKeys.map((name) => ( ))}
) }) export function AlertContent({ alertKey, data: alertData, system, alert, global = false, overwriteExisting = false, initialAlertsState = {}, }: { alertKey: string data: AlertInfo system: SystemRecord alert?: AlertRecord global?: boolean overwriteExisting?: boolean initialAlertsState?: Record> }) { const { name } = alertData const singleDescription = alertData.singleDesc?.() const [checked, setChecked] = useState(global ? false : !!alert) const [min, setMin] = useState(alert?.min || 10) const [value, setValue] = useState(alert?.value || (singleDescription ? 0 : alertData.start ?? 80)) const Icon = alertData.icon /** Get system ids to update */ function getSystemIds(): string[] { // if not global, update only the current system if (!global) { return [system.id] } // if global, update all systems when overwriteExisting is true // update only systems without an existing alert when overwriteExisting is false const allSystems = $systems.get() const systemIds: string[] = [] for (const system of allSystems) { if (overwriteExisting || !initialAlertsState[system.id]?.has(alertKey)) { systemIds.push(system.id) } } return systemIds } function sendUpsert(min: number, value: number) { const systems = getSystemIds() systems.length && upsertAlerts({ name: alertKey, value, min, systems, }) } return (
{checked && (
}> {!singleDescription && (

Average exceeds{" "} {value} {alertData.unit}

sendUpsert(min, val[0])} onValueChange={(val) => setValue(val[0])} step={alertData.step ?? 1} min={alertData.min ?? 1} max={alertData.max ?? 99} />
)}

{singleDescription && ( <> {singleDescription} {` `} )} For {min}{" "}

sendUpsert(minVal[0], value)} onValueChange={(val) => setMin(val[0])} min={1} max={60} />
)}
) }