import { t } from "@lingui/core/macro" import { Trans } from "@lingui/react/macro" import { BellIcon, LoaderCircleIcon, PlusIcon, SaveIcon, Trash2Icon } from "lucide-react" import { type ChangeEventHandler, useEffect, useState } from "react" import * as v from "valibot" import { prependBasePath } from "@/components/router" import { Button } from "@/components/ui/button" import { Card } from "@/components/ui/card" import { Input } from "@/components/ui/input" import { InputTags } from "@/components/ui/input-tags" import { Label } from "@/components/ui/label" import { Separator } from "@/components/ui/separator" import { toast } from "@/components/ui/use-toast" import { isAdmin, pb } from "@/lib/api" import type { UserSettings } from "@/types" import { saveSettings } from "./layout" import { QuietHours } from "./quiet-hours" interface ShoutrrrUrlCardProps { url: string onUrlChange: ChangeEventHandler onRemove: () => void } const NotificationSchema = v.object({ emails: v.array(v.pipe(v.string(), v.email())), webhooks: v.array(v.pipe(v.string(), v.url())), }) const SettingsNotificationsPage = ({ userSettings }: { userSettings: UserSettings }) => { const [webhooks, setWebhooks] = useState(userSettings.webhooks ?? []) const [emails, setEmails] = useState(userSettings.emails ?? []) const [isLoading, setIsLoading] = useState(false) // update values when userSettings changes useEffect(() => { setWebhooks(userSettings.webhooks ?? []) setEmails(userSettings.emails ?? []) }, [userSettings]) function addWebhook() { setWebhooks([...webhooks, ""]) // focus on the new input queueMicrotask(() => { const inputs = document.querySelectorAll("#webhooks input") as NodeListOf inputs[inputs.length - 1]?.focus() }) } const removeWebhook = (index: number) => setWebhooks(webhooks.filter((_, i) => i !== index)) function updateWebhook(index: number, value: string) { const newWebhooks = [...webhooks] newWebhooks[index] = value setWebhooks(newWebhooks) } async function updateSettings() { setIsLoading(true) try { const parsedData = v.parse(NotificationSchema, { emails, webhooks }) await saveSettings(parsedData) } catch (e: any) { toast({ title: t`Failed to save settings`, description: e.message, variant: "destructive", }) } setIsLoading(false) } return (

Notifications

Configure how you receive alert notifications.

Looking instead for where to create alerts? Click the bell icons in the systems table.

Email notifications

{isAdmin() && (

Please{" "} configure an SMTP server {" "} to ensure alerts are delivered.

)}

Save address using enter key or comma. Leave blank to disable email notifications.

Webhook / Push notifications

Beszel uses{" "} Shoutrrr {" "} to integrate with popular notification services.

{webhooks.length > 0 && (
{webhooks.map((webhook, index) => ( ) => updateWebhook(index, e.target.value)} onRemove={() => removeWebhook(index)} /> ))}
)}
) } const ShoutrrrUrlCard = ({ url, onUrlChange, onRemove }: ShoutrrrUrlCardProps) => { const [isLoading, setIsLoading] = useState(false) const sendTestNotification = async () => { setIsLoading(true) const res = await pb.send("/api/beszel/test-notification", { method: "POST", body: { url } }) if ("err" in res && !res.err) { toast({ title: t`Test notification sent`, description: t`Check your notification service`, }) } else { toast({ title: t`Error`, description: res.err ?? t`Failed to send test notification`, variant: "destructive", }) } setIsLoading(false) } return (
) } export default SettingsNotificationsPage