mirror of
https://github.com/fankes/beszel.git
synced 2025-10-20 10:19:27 +08:00
updates to settings page and alerts
This commit is contained in:
@@ -29,7 +29,7 @@ type AlertData struct {
|
|||||||
LinkText string
|
LinkText string
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserAlertSettings struct {
|
type UserNotificationSettings struct {
|
||||||
Emails []string `json:"emails"`
|
Emails []string `json:"emails"`
|
||||||
Webhooks []string `json:"webhooks"`
|
Webhooks []string `json:"webhooks"`
|
||||||
}
|
}
|
||||||
@@ -166,16 +166,16 @@ func (am *AlertManager) sendAlert(data AlertData) {
|
|||||||
dbx.Params{"user": data.UserID},
|
dbx.Params{"user": data.UserID},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Failed to get user settings", "err", err.Error())
|
am.app.Logger().Error("Failed to get user settings", "err", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// unmarshal user settings
|
// unmarshal user settings
|
||||||
userAlertSettings := UserAlertSettings{
|
userAlertSettings := UserNotificationSettings{
|
||||||
Emails: []string{},
|
Emails: []string{},
|
||||||
Webhooks: []string{},
|
Webhooks: []string{},
|
||||||
}
|
}
|
||||||
if err := record.UnmarshalJSONField("settings", &userAlertSettings); err != nil {
|
if err := record.UnmarshalJSONField("settings", &userAlertSettings); err != nil {
|
||||||
log.Println("Failed to unmarshal user settings", "err", err.Error())
|
am.app.Logger().Error("Failed to unmarshal user settings", "err", err.Error())
|
||||||
}
|
}
|
||||||
// send alerts via webhooks
|
// send alerts via webhooks
|
||||||
for _, webhook := range userAlertSettings.Webhooks {
|
for _, webhook := range userAlertSettings.Webhooks {
|
||||||
@@ -186,13 +186,12 @@ func (am *AlertManager) sendAlert(data AlertData) {
|
|||||||
}
|
}
|
||||||
// send alerts via email
|
// send alerts via email
|
||||||
if len(userAlertSettings.Emails) == 0 {
|
if len(userAlertSettings.Emails) == 0 {
|
||||||
log.Println("No email addresses found")
|
// log.Println("No email addresses found")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
addresses := []mail.Address{}
|
addresses := []mail.Address{}
|
||||||
for _, email := range userAlertSettings.Emails {
|
for _, email := range userAlertSettings.Emails {
|
||||||
addresses = append(addresses, mail.Address{Address: email})
|
addresses = append(addresses, mail.Address{Address: email})
|
||||||
log.Println("Sending alert via email to", email)
|
|
||||||
}
|
}
|
||||||
message := mailer.Message{
|
message := mailer.Message{
|
||||||
To: addresses,
|
To: addresses,
|
||||||
@@ -211,6 +210,7 @@ func (am *AlertManager) sendAlert(data AlertData) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendShoutrrrAlert sends an alert via a Shoutrrr URL
|
||||||
func (am *AlertManager) SendShoutrrrAlert(notificationUrl, title, message, link, linkText string) error {
|
func (am *AlertManager) SendShoutrrrAlert(notificationUrl, title, message, link, linkText string) error {
|
||||||
// services that support title param
|
// services that support title param
|
||||||
supportsTitle := []string{"bark", "discord", "gotify", "ifttt", "join", "matrix", "ntfy", "opsgenie", "pushbullet", "pushover", "slack", "teams", "telegram", "zulip"}
|
supportsTitle := []string{"bark", "discord", "gotify", "ifttt", "join", "matrix", "ntfy", "opsgenie", "pushbullet", "pushover", "slack", "teams", "telegram", "zulip"}
|
||||||
@@ -259,7 +259,7 @@ func (am *AlertManager) SendShoutrrrAlert(notificationUrl, title, message, link,
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
am.app.Logger().Info("Sent shoutrrr alert", "title", title)
|
am.app.Logger().Info("Sent shoutrrr alert", "title", title)
|
||||||
} else {
|
} else {
|
||||||
am.app.Logger().Error("Error sending shoutrrr alert", "errs", err)
|
am.app.Logger().Error("Error sending shoutrrr alert", "err", err.Error())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@@ -13,6 +13,7 @@ import { LoaderCircleIcon, SaveIcon } from 'lucide-react'
|
|||||||
import { UserSettings } from '@/types'
|
import { UserSettings } from '@/types'
|
||||||
import { saveSettings } from './layout'
|
import { saveSettings } from './layout'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
// import { Input } from '@/components/ui/input'
|
||||||
|
|
||||||
export default function SettingsProfilePage({ userSettings }: { userSettings: UserSettings }) {
|
export default function SettingsProfilePage({ userSettings }: { userSettings: UserSettings }) {
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
@@ -30,17 +31,22 @@ export default function SettingsProfilePage({ userSettings }: { userSettings: Us
|
|||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-xl font-medium mb-2">General</h3>
|
<h3 className="text-xl font-medium mb-2">General</h3>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground leading-relaxed">
|
||||||
Set your preferred language and chart display options.
|
Change general application options.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Separator className="my-4" />
|
<Separator className="my-4" />
|
||||||
<form onSubmit={handleSubmit} className="space-y-5">
|
<form onSubmit={handleSubmit} className="space-y-5">
|
||||||
|
{/* <Separator />
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<h3 className="mb-1 text-lg font-medium">Language</h3>
|
<h3 className="mb-1 text-lg font-medium">Language</h3>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground leading-relaxed">
|
||||||
Additional language support coming soon.
|
Internationalization will be added in a future release. Please see the{' '}
|
||||||
|
<a href="#" className="link" target="_blank">
|
||||||
|
discussion on GitHub
|
||||||
|
</a>{' '}
|
||||||
|
for more details.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Label className="block" htmlFor="lang">
|
<Label className="block" htmlFor="lang">
|
||||||
@@ -54,12 +60,13 @@ export default function SettingsProfilePage({ userSettings }: { userSettings: Us
|
|||||||
<SelectItem value="en">English</SelectItem>
|
<SelectItem value="en">English</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div> */}
|
||||||
<Separator />
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<h3 className="mb-1 text-lg font-medium">Chart options</h3>
|
<h3 className="mb-1 text-lg font-medium">Chart options</h3>
|
||||||
<p className="text-sm text-muted-foreground">Adjust display options for charts.</p>
|
<p className="text-sm text-muted-foreground leading-relaxed">
|
||||||
|
Adjust display options for charts.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Label className="block" htmlFor="chartTime">
|
<Label className="block" htmlFor="chartTime">
|
||||||
Default time period
|
Default time period
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Suspense, lazy, useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { Separator } from '../../ui/separator'
|
import { Separator } from '../../ui/separator'
|
||||||
import { SidebarNav } from './sidebar-nav.tsx'
|
import { SidebarNav } from './sidebar-nav.tsx'
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card.tsx'
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card.tsx'
|
||||||
@@ -9,9 +9,8 @@ import { BellIcon, SettingsIcon } from 'lucide-react'
|
|||||||
import { $userSettings, pb } from '@/lib/stores.ts'
|
import { $userSettings, pb } from '@/lib/stores.ts'
|
||||||
import { toast } from '@/components/ui/use-toast.ts'
|
import { toast } from '@/components/ui/use-toast.ts'
|
||||||
import { UserSettings } from '@/types.js'
|
import { UserSettings } from '@/types.js'
|
||||||
|
import General from './general.tsx'
|
||||||
const General = lazy(() => import('./general.tsx'))
|
import Notifications from './notifications.tsx'
|
||||||
const Notifications = lazy(() => import('./notifications.tsx'))
|
|
||||||
|
|
||||||
const sidebarNavItems = [
|
const sidebarNavItems = [
|
||||||
{
|
{
|
||||||
@@ -27,31 +26,28 @@ const sidebarNavItems = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
export async function saveSettings(newSettings: Partial<UserSettings>) {
|
export async function saveSettings(newSettings: Partial<UserSettings>) {
|
||||||
// console.log('Updating settings:', newSettings)
|
|
||||||
try {
|
try {
|
||||||
// get fresh copy of settings
|
// get fresh copy of settings
|
||||||
const req = await pb.collection('user_settings').getFirstListItem('', {
|
const req = await pb.collection('user_settings').getFirstListItem('', {
|
||||||
fields: 'id,settings',
|
fields: 'id,settings',
|
||||||
})
|
})
|
||||||
// make new user settings
|
|
||||||
const mergedSettings = {
|
|
||||||
...req.settings,
|
|
||||||
...newSettings,
|
|
||||||
}
|
|
||||||
// update user settings
|
// update user settings
|
||||||
const updatedSettings = await pb.collection('user_settings').update(req.id, {
|
const updatedSettings = await pb.collection('user_settings').update(req.id, {
|
||||||
settings: mergedSettings,
|
settings: {
|
||||||
|
...req.settings,
|
||||||
|
...newSettings,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
$userSettings.set(updatedSettings.settings)
|
$userSettings.set(updatedSettings.settings)
|
||||||
toast({
|
toast({
|
||||||
title: 'Settings saved',
|
title: 'Settings saved',
|
||||||
description: 'Your notification settings have been updated.',
|
description: 'Your user settings have been updated.',
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('update settings', e)
|
// console.error('update settings', e)
|
||||||
toast({
|
toast({
|
||||||
title: 'Failed to save settings',
|
title: 'Failed to save settings',
|
||||||
description: 'Please check logs for more details.',
|
description: 'Check logs for more details.',
|
||||||
variant: 'destructive',
|
variant: 'destructive',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -72,19 +68,17 @@ export default function SettingsLayout() {
|
|||||||
<Card className="pt-5 px-4 pb-8 sm:pt-6 sm:px-7">
|
<Card className="pt-5 px-4 pb-8 sm:pt-6 sm:px-7">
|
||||||
<CardHeader className="p-0">
|
<CardHeader className="p-0">
|
||||||
<CardTitle className="mb-1">Settings</CardTitle>
|
<CardTitle className="mb-1">Settings</CardTitle>
|
||||||
<CardDescription>Manage notification and display preferences.</CardDescription>
|
<CardDescription>Manage display and notification preferences.</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="p-0">
|
<CardContent className="p-0">
|
||||||
<Separator className="my-3 md:my-5" />
|
<Separator className="hidden md:block my-5" />
|
||||||
<div className="flex flex-col gap-3 md:flex-row md:gap-5 lg:gap-10">
|
<div className="flex flex-col gap-3.5 md:flex-row md:gap-5 lg:gap-10">
|
||||||
<aside className="md:w-48 w-full">
|
<aside className="md:w-48 w-full">
|
||||||
<SidebarNav items={sidebarNavItems} />
|
<SidebarNav items={sidebarNavItems} />
|
||||||
</aside>
|
</aside>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<Suspense>
|
{/* @ts-ignore */}
|
||||||
{/* @ts-ignore */}
|
<SettingsContent name={page?.params?.name ?? 'general'} />
|
||||||
<SettingsContent name={page?.params?.name ?? 'general'} />
|
|
||||||
</Suspense>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
@@ -101,5 +95,4 @@ function SettingsContent({ name }: { name: string }) {
|
|||||||
case 'notifications':
|
case 'notifications':
|
||||||
return <Notifications userSettings={userSettings} />
|
return <Notifications userSettings={userSettings} />
|
||||||
}
|
}
|
||||||
return ''
|
|
||||||
}
|
}
|
||||||
|
@@ -11,6 +11,7 @@ import { InputTags } from '@/components/ui/input-tags'
|
|||||||
import { UserSettings } from '@/types'
|
import { UserSettings } from '@/types'
|
||||||
import { saveSettings } from './layout'
|
import { saveSettings } from './layout'
|
||||||
import * as v from 'valibot'
|
import * as v from 'valibot'
|
||||||
|
import { isAdmin } from '@/lib/utils'
|
||||||
|
|
||||||
interface ShoutrrrUrlCardProps {
|
interface ShoutrrrUrlCardProps {
|
||||||
url: string
|
url: string
|
||||||
@@ -41,7 +42,6 @@ const SettingsNotificationsPage = ({ userSettings }: { userSettings: UserSetting
|
|||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
try {
|
try {
|
||||||
const parsedData = v.parse(NotificationSchema, { emails, webhooks })
|
const parsedData = v.parse(NotificationSchema, { emails, webhooks })
|
||||||
console.log('parsedData', parsedData)
|
|
||||||
await saveSettings(parsedData)
|
await saveSettings(parsedData)
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
toast({
|
toast({
|
||||||
@@ -57,12 +57,12 @@ const SettingsNotificationsPage = ({ userSettings }: { userSettings: UserSetting
|
|||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-xl font-medium mb-2">Notifications</h3>
|
<h3 className="text-xl font-medium mb-2">Notifications</h3>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground leading-relaxed">
|
||||||
Configure how you receive alert notifications.
|
Configure how you receive alert notifications.
|
||||||
</p>
|
</p>
|
||||||
<p className="text-sm text-muted-foreground mt-1.5">
|
<p className="text-sm text-muted-foreground mt-1.5 leading-relaxed">
|
||||||
Looking for where to create system alerts? Click the bell icons{' '}
|
Looking instead for where to create system alerts? Click the bell{' '}
|
||||||
<BellIcon className="inline h-4 w-4" /> in the dashboard table.
|
<BellIcon className="inline h-4 w-4" /> icons in the systems table.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Separator className="my-4" />
|
<Separator className="my-4" />
|
||||||
@@ -70,26 +70,36 @@ const SettingsNotificationsPage = ({ userSettings }: { userSettings: UserSetting
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<h3 className="mb-1 text-lg font-medium">Email notifications</h3>
|
<h3 className="mb-1 text-lg font-medium">Email notifications</h3>
|
||||||
<p className="text-sm text-muted-foreground">
|
{isAdmin() && (
|
||||||
Leave blank to disable email notifications.
|
<p className="text-sm text-muted-foreground leading-relaxed">
|
||||||
</p>
|
Please{' '}
|
||||||
|
<a href="/_/#/settings/mail" className="link" target="_blank">
|
||||||
|
configure an SMTP server
|
||||||
|
</a>{' '}
|
||||||
|
to ensure alerts are delivered.{' '}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<Label className="block">To email(s)</Label>
|
<Label className="block" htmlFor="email">
|
||||||
|
To email(s)
|
||||||
|
</Label>
|
||||||
<InputTags
|
<InputTags
|
||||||
value={emails}
|
value={emails}
|
||||||
onChange={setEmails}
|
onChange={setEmails}
|
||||||
placeholder="Enter email address..."
|
placeholder="Enter email address..."
|
||||||
className="w-full"
|
className="w-full"
|
||||||
|
type="email"
|
||||||
|
id="email"
|
||||||
/>
|
/>
|
||||||
<p className="text-[0.8rem] text-muted-foreground">
|
<p className="text-[0.8rem] text-muted-foreground">
|
||||||
Save address using enter key or comma.
|
Save address using enter key or comma. Leave blank to disable email notifications.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Separator />
|
<Separator />
|
||||||
<div className="space-y-4">
|
<div className="space-y-3">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="mb-1 text-lg font-medium">Webhook / Push notifications</h3>
|
<h3 className="mb-1 text-lg font-medium">Webhook / Push notifications</h3>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground leading-relaxed">
|
||||||
Beszel uses{' '}
|
Beszel uses{' '}
|
||||||
<a
|
<a
|
||||||
href="https://containrrr.dev/shoutrrr/services/overview/"
|
href="https://containrrr.dev/shoutrrr/services/overview/"
|
||||||
@@ -102,7 +112,7 @@ const SettingsNotificationsPage = ({ userSettings }: { userSettings: UserSetting
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{webhooks.length > 0 && (
|
{webhooks.length > 0 && (
|
||||||
<div className="grid gap-3">
|
<div className="grid gap-2.5">
|
||||||
{webhooks.map((webhook, index) => (
|
{webhooks.map((webhook, index) => (
|
||||||
<ShoutrrrUrlCard
|
<ShoutrrrUrlCard
|
||||||
key={index}
|
key={index}
|
||||||
|
@@ -27,8 +27,8 @@ export function SidebarNav({ className, items, ...props }: SidebarNavProps) {
|
|||||||
<>
|
<>
|
||||||
{/* Mobile View */}
|
{/* Mobile View */}
|
||||||
<div className="md:hidden">
|
<div className="md:hidden">
|
||||||
<Select onValueChange={(value: string) => navigate(value)} defaultValue={page?.path}>
|
<Select onValueChange={(value: string) => navigate(value)} value={page?.path}>
|
||||||
<SelectTrigger className="w-full mb-3">
|
<SelectTrigger className="w-full my-3.5">
|
||||||
<SelectValue placeholder="Select a page" />
|
<SelectValue placeholder="Select a page" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
|
@@ -260,8 +260,8 @@ export default function SystemDetail({ name }: { name: string }) {
|
|||||||
<Tooltip>
|
<Tooltip>
|
||||||
<Separator orientation="vertical" className="h-4 bg-primary/30" />
|
<Separator orientation="vertical" className="h-4 bg-primary/30" />
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<div className="flex gap-1.5">
|
<div className="flex gap-1.5 items-center">
|
||||||
<ClockArrowUp className="h-4 w-4 mt-[1px]" /> {uptime}
|
<ClockArrowUp className="h-4 w-4" /> {uptime}
|
||||||
</div>
|
</div>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>Uptime</TooltipContent>
|
<TooltipContent>Uptime</TooltipContent>
|
||||||
@@ -271,8 +271,8 @@ export default function SystemDetail({ name }: { name: string }) {
|
|||||||
{system.info?.m && (
|
{system.info?.m && (
|
||||||
<>
|
<>
|
||||||
<Separator orientation="vertical" className="h-4 bg-primary/30" />
|
<Separator orientation="vertical" className="h-4 bg-primary/30" />
|
||||||
<div className="flex gap-1.5">
|
<div className="flex gap-1.5 items-center">
|
||||||
<CpuIcon className="h-4 w-4 mt-[1px]" />
|
<CpuIcon className="h-4 w-4" />
|
||||||
{system.info.m} ({system.info.c}c / {system.info.t}t)
|
{system.info.m} ({system.info.c}c / {system.info.t}t)
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
@@ -324,7 +324,7 @@ export default function SystemsTable({ filter }: { filter?: string }) {
|
|||||||
? 'auto'
|
? 'auto'
|
||||||
: cell.column.getSize(),
|
: cell.column.getSize(),
|
||||||
}}
|
}}
|
||||||
className={'overflow-hidden relative py-2.5'}
|
className={cn('overflow-hidden relative', data.length > 10 ? 'py-2' : 'py-2.5')}
|
||||||
>
|
>
|
||||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
@@ -15,6 +15,7 @@ import { Switch } from '@/components/ui/switch'
|
|||||||
import { AlertRecord, SystemRecord } from '@/types'
|
import { AlertRecord, SystemRecord } from '@/types'
|
||||||
import { lazy, Suspense, useMemo, useState } from 'react'
|
import { lazy, Suspense, useMemo, useState } from 'react'
|
||||||
import { toast } from './ui/use-toast'
|
import { toast } from './ui/use-toast'
|
||||||
|
import { Link } from './router'
|
||||||
|
|
||||||
const Slider = lazy(() => import('./ui/slider'))
|
const Slider = lazy(() => import('./ui/slider'))
|
||||||
|
|
||||||
@@ -49,17 +50,13 @@ export default function AlertsButton({ system }: { system: SystemRecord }) {
|
|||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="max-h-full overflow-auto">
|
<DialogContent className="max-h-full overflow-auto">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="mb-1">Alerts for {system.name}</DialogTitle>
|
<DialogTitle className="text-xl">{system.name} alerts</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription className="mb-1">
|
||||||
{isAdmin() && (
|
See{' '}
|
||||||
<span>
|
<Link href="/settings/notifications" className="link">
|
||||||
Please{' '}
|
notification settings
|
||||||
<a href="/_/#/settings/mail" className="link">
|
</Link>{' '}
|
||||||
configure an SMTP server
|
to configure how you receive alerts.
|
||||||
</a>{' '}
|
|
||||||
to ensure alerts are delivered.{' '}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="grid gap-3">
|
<div className="grid gap-3">
|
||||||
@@ -83,7 +80,7 @@ export default function AlertsButton({ system }: { system: SystemRecord }) {
|
|||||||
alerts={systemAlerts}
|
alerts={systemAlerts}
|
||||||
name="Disk"
|
name="Disk"
|
||||||
title="Disk Usage"
|
title="Disk Usage"
|
||||||
description="Triggers when disk usage exceeds a threshold."
|
description="Triggers when root usage exceeds a threshold."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
Reference in New Issue
Block a user