871 lines
40 KiB
TypeScript
Raw Normal View History

"use client"
import { AppSidebar } from "@/components/app-sidebar"
2025-04-12 20:01:43 +02:00
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@/components/ui/breadcrumb"
import { Separator } from "@/components/ui/separator"
import { SidebarInset, SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"
import { Card, CardContent, CardHeader } from "@/components/ui/card"
import { useTheme } from "next-themes"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
2025-04-14 20:59:41 +02:00
import { Input } from "@/components/ui/input"
import { useEffect, useState } from "react"
import axios from "axios"
import Cookies from "js-cookie"
import { Button } from "@/components/ui/button"
2025-04-14 20:59:41 +02:00
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
2025-04-29 20:27:21 +02:00
import { AlertCircle, Check, Palette, User, Bell, AtSign, Send, MessageSquare, Trash2, Play, Languages } from "lucide-react"
2025-04-23 21:34:52 +02:00
import { Toaster } from "@/components/ui/sonner"
import { toast } from "sonner"
import { Textarea } from "@/components/ui/textarea"
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog"
import { Label } from "@/components/ui/label"
import { Checkbox } from "@/components/ui/checkbox"
2025-04-29 19:38:48 +02:00
import { useTranslations } from "next-intl"
2025-04-12 20:01:43 +02:00
2025-04-17 15:39:21 +02:00
interface NotificationsResponse {
notifications: any[]
2025-04-17 15:39:21 +02:00
}
2025-04-17 17:27:01 +02:00
interface NotificationResponse {
notification_text_application?: string
notification_text_server?: string
2025-04-17 17:27:01 +02:00
}
2025-04-17 15:39:21 +02:00
2025-04-12 20:01:43 +02:00
export default function Settings() {
2025-04-29 19:38:48 +02:00
const t = useTranslations()
const { theme, setTheme } = useTheme()
2025-04-12 20:01:43 +02:00
2025-04-14 20:59:41 +02:00
const [email, setEmail] = useState<string>("")
const [password, setPassword] = useState<string>("")
const [confirmPassword, setConfirmPassword] = useState<string>("")
const [oldPassword, setOldPassword] = useState<string>("")
const [emailError, setEmailError] = useState<string>("")
const [passwordError, setPasswordError] = useState<string>("")
const [emailErrorVisible, setEmailErrorVisible] = useState<boolean>(false)
const [passwordErrorVisible, setPasswordErrorVisible] = useState<boolean>(false)
const [passwordSuccess, setPasswordSuccess] = useState<boolean>(false)
const [emailSuccess, setEmailSuccess] = useState<boolean>(false)
const [notificationType, setNotificationType] = useState<string>("")
2025-04-27 14:56:40 +02:00
const [notificationName, setNotificationName] = useState<string>("")
2025-04-17 15:14:27 +02:00
const [smtpHost, setSmtpHost] = useState<string>("")
const [smtpPort, setSmtpPort] = useState<number>(0)
const [smtpSecure, setSmtpSecure] = useState<boolean>(false)
const [smtpUsername, setSmtpUsername] = useState<string>("")
const [smtpPassword, setSmtpPassword] = useState<string>("")
const [smtpFrom, setSmtpFrom] = useState<string>("")
const [smtpTo, setSmtpTo] = useState<string>("")
const [telegramToken, setTelegramToken] = useState<string>("")
const [telegramChatId, setTelegramChatId] = useState<string>("")
const [discordWebhook, setDiscordWebhook] = useState<string>("")
const [gotifyUrl, setGotifyUrl] = useState<string>("")
const [gotifyToken, setGotifyToken] = useState<string>("")
const [ntfyUrl, setNtfyUrl] = useState<string>("")
const [ntfyToken, setNtfyToken] = useState<string>("")
2025-04-21 15:31:38 +02:00
const [pushoverUrl, setPushoverUrl] = useState<string>("")
const [pushoverToken, setPushoverToken] = useState<string>("")
const [pushoverUser, setPushoverUser] = useState<string>("")
const [echobellURL, setEchobellURL] = useState<string>("")
2025-04-29 20:14:44 +02:00
const [language, setLanguage] = useState<string>("english")
const [notifications, setNotifications] = useState<any[]>([])
const [notificationTextApplication, setNotificationTextApplication] = useState<string>("")
const [notificationTextServer, setNotificationTextServer] = useState<string>("")
2025-04-17 17:27:01 +02:00
2025-04-14 20:59:41 +02:00
const changeEmail = async () => {
setEmailErrorVisible(false)
setEmailSuccess(false)
setEmailError("")
2025-04-14 20:59:41 +02:00
if (!email) {
2025-04-29 19:38:48 +02:00
setEmailError(t('Settings.UserSettings.ChangeEmail.EmailRequired'))
setEmailErrorVisible(true)
2025-04-14 20:59:41 +02:00
setTimeout(() => {
setEmailErrorVisible(false)
setEmailError("")
}, 3000)
return
2025-04-14 20:59:41 +02:00
}
try {
await axios.post("/api/auth/edit_email", {
2025-04-14 20:59:41 +02:00
newEmail: email,
jwtToken: Cookies.get("token"),
})
setEmailSuccess(true)
setEmail("")
2025-04-14 20:59:41 +02:00
setTimeout(() => {
setEmailSuccess(false)
}, 3000)
2025-04-14 20:59:41 +02:00
} catch (error: any) {
setEmailError(error.response.data.error)
setEmailErrorVisible(true)
2025-04-14 20:59:41 +02:00
setTimeout(() => {
setEmailErrorVisible(false)
setEmailError("")
}, 3000)
2025-04-14 20:59:41 +02:00
}
}
const changePassword = async () => {
try {
if (password !== confirmPassword) {
2025-04-29 19:38:48 +02:00
setPasswordError(t('Settings.UserSettings.ChangePassword.PasswordsDontMatch'))
setPasswordErrorVisible(true)
2025-04-14 20:59:41 +02:00
setTimeout(() => {
setPasswordErrorVisible(false)
setPasswordError("")
}, 3000)
return
2025-04-14 20:59:41 +02:00
}
if (!oldPassword || !password || !confirmPassword) {
2025-04-29 19:38:48 +02:00
setPasswordError(t('Settings.UserSettings.ChangePassword.AllFieldsRequired'))
setPasswordErrorVisible(true)
2025-04-14 20:59:41 +02:00
setTimeout(() => {
setPasswordErrorVisible(false)
setPasswordError("")
}, 3000)
return
2025-04-14 20:59:41 +02:00
}
const response = await axios.post("/api/auth/edit_password", {
2025-04-14 20:59:41 +02:00
oldPassword: oldPassword,
newPassword: password,
jwtToken: Cookies.get("token"),
})
2025-04-14 20:59:41 +02:00
if (response.status === 200) {
setPasswordSuccess(true)
setPassword("")
setOldPassword("")
setConfirmPassword("")
2025-04-14 20:59:41 +02:00
setTimeout(() => {
setPasswordSuccess(false)
}, 3000)
2025-04-14 20:59:41 +02:00
}
} catch (error: any) {
setPasswordErrorVisible(true)
setPasswordError(error.response.data.error)
2025-04-14 20:59:41 +02:00
setTimeout(() => {
setPasswordErrorVisible(false)
setPasswordError("")
}, 3000)
2025-04-14 20:59:41 +02:00
}
}
2025-04-17 15:14:27 +02:00
const addNotification = async () => {
try {
const response = await axios.post("/api/notifications/add", {
2025-04-27 14:56:40 +02:00
name: notificationName,
2025-04-17 15:14:27 +02:00
type: notificationType,
smtpHost: smtpHost,
smtpPort: smtpPort,
smtpSecure: smtpSecure,
smtpUsername: smtpUsername,
smtpPassword: smtpPassword,
smtpFrom: smtpFrom,
smtpTo: smtpTo,
telegramToken: telegramToken,
telegramChatId: telegramChatId,
discordWebhook: discordWebhook,
gotifyUrl: gotifyUrl,
gotifyToken: gotifyToken,
ntfyUrl: ntfyUrl,
ntfyToken: ntfyToken,
2025-04-21 15:31:38 +02:00
pushoverUrl: pushoverUrl,
pushoverToken: pushoverToken,
pushoverUser: pushoverUser,
echobellURL: echobellURL,
})
getNotifications()
} catch (error: any) {
alert(error.response.data.error)
2025-04-17 15:14:27 +02:00
}
}
const deleteNotification = async (id: number) => {
try {
const response = await axios.post("/api/notifications/delete", {
id: id,
})
2025-04-17 15:14:27 +02:00
if (response.status === 200) {
getNotifications()
2025-04-17 15:14:27 +02:00
}
} catch (error: any) {
alert(error.response.data.error)
2025-04-17 15:14:27 +02:00
}
}
const getNotifications = async () => {
try {
const response = await axios.post<NotificationsResponse>("/api/notifications/get", {})
if (response.status === 200 && response.data) {
setNotifications(response.data.notifications)
}
} catch (error: any) {
alert(error.response.data.error)
}
}
useEffect(() => {
getNotifications()
}, [])
2025-04-17 17:27:01 +02:00
const getNotificationText = async () => {
try {
const response = await axios.post<NotificationResponse>("/api/settings/get_notification_text", {})
2025-04-17 17:27:01 +02:00
if (response.status === 200) {
if (response.data.notification_text_application) {
setNotificationTextApplication(response.data.notification_text_application)
2025-04-17 17:27:01 +02:00
} else {
setNotificationTextApplication("The application !name (!url) is now !status.")
}
if (response.data.notification_text_server) {
setNotificationTextServer(response.data.notification_text_server)
} else {
setNotificationTextServer("The server !name is now !status.")
2025-04-17 17:27:01 +02:00
}
}
} catch (error: any) {
alert(error.response.data.error)
2025-04-17 17:27:01 +02:00
}
}
2025-04-17 17:27:01 +02:00
const editNotificationText = async () => {
try {
const response = await axios.post("/api/settings/notification_text", {
text_application: notificationTextApplication,
text_server: notificationTextServer,
})
2025-04-17 17:27:01 +02:00
} catch (error: any) {
alert(error.response.data.error)
2025-04-17 17:27:01 +02:00
}
}
useEffect(() => {
getNotificationText()
}, [])
2025-04-23 21:34:52 +02:00
const testNotification = async (id: number) => {
try {
const response = await axios.post("/api/notifications/test", {
notificationId: id,
})
2025-04-29 19:38:48 +02:00
toast.success(t('Settings.Notifications.TestSuccess'))
2025-04-23 21:34:52 +02:00
} catch (error: any) {
toast.error(error.response.data.error)
}
}
2025-04-29 20:14:44 +02:00
useEffect(() => {
const language = Cookies.get("language")
if (language === "en") {
setLanguage("english")
} else if (language === "de") {
setLanguage("german")
}
}, [])
const setLanguageFunc = (value: string) => {
setLanguage(value)
if (value === "english") {
Cookies.set("language", "en")
} else if (value === "german") {
Cookies.set("language", "de")
}
// Reload the page
window.location.reload()
}
2025-04-12 20:01:43 +02:00
return (
<SidebarProvider>
<AppSidebar />
<SidebarInset>
<header className="flex h-16 shrink-0 items-center gap-2 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
2025-04-12 20:01:43 +02:00
<div className="flex items-center gap-2 px-4">
<SidebarTrigger className="-ml-1" />
<Separator orientation="vertical" className="mr-2 h-4" />
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem className="hidden md:block">
2025-04-29 19:38:48 +02:00
<BreadcrumbPage>{t('Settings.Breadcrumb.Dashboard')}</BreadcrumbPage>
2025-04-12 20:01:43 +02:00
</BreadcrumbItem>
<BreadcrumbSeparator className="hidden md:block" />
<BreadcrumbItem>
2025-04-29 19:38:48 +02:00
<BreadcrumbPage>{t('Settings.Breadcrumb.Settings')}</BreadcrumbPage>
2025-04-12 20:01:43 +02:00
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
</div>
</header>
<div className="p-6">
2025-04-15 16:18:35 +02:00
<div className="pb-4">
2025-04-29 19:38:48 +02:00
<span className="text-3xl font-bold">{t('Settings.Title')}</span>
2025-04-15 16:18:35 +02:00
</div>
<div className="grid gap-6">
<Card className="overflow-hidden border-2 border-muted/20 shadow-sm">
<CardHeader className="bg-muted/10 px-6 py-4 border-b">
<div className="flex items-center gap-2">
<User className="h-5 w-5 text-primary" />
2025-04-29 19:38:48 +02:00
<h2 className="text-xl font-semibold">{t('Settings.UserSettings.Title')}</h2>
2025-04-15 16:18:35 +02:00
</div>
2025-04-12 20:01:43 +02:00
</CardHeader>
<CardContent className="pb-6">
2025-04-15 16:18:35 +02:00
<div className="text-sm text-muted-foreground mb-6">
2025-04-29 19:38:48 +02:00
{t('Settings.UserSettings.Description')}
2025-04-15 16:18:35 +02:00
</div>
<div className="grid md:grid-cols-2 gap-8">
<div className="space-y-4">
<div className="border-b pb-2">
2025-04-29 19:38:48 +02:00
<h3 className="font-semibold text-lg">{t('Settings.UserSettings.ChangeEmail.Title')}</h3>
2025-04-15 16:18:35 +02:00
</div>
{emailErrorVisible && (
<Alert variant="destructive" className="animate-in fade-in-50">
<AlertCircle className="h-4 w-4" />
2025-04-29 19:38:48 +02:00
<AlertTitle>{t('Common.Error')}</AlertTitle>
2025-04-15 16:18:35 +02:00
<AlertDescription>{emailError}</AlertDescription>
</Alert>
)}
{emailSuccess && (
<Alert className="border-green-200 bg-green-50 text-green-800 dark:border-green-800 dark:bg-green-950 dark:text-green-300 animate-in fade-in-50">
<Check className="h-4 w-4" />
2025-04-29 19:38:48 +02:00
<AlertTitle>{t('Settings.UserSettings.ChangeEmail.Success')}</AlertTitle>
2025-04-15 16:18:35 +02:00
</Alert>
)}
<div className="space-y-3">
<Input
type="email"
2025-04-29 19:38:48 +02:00
placeholder={t('Settings.UserSettings.ChangeEmail.Placeholder')}
2025-04-15 16:18:35 +02:00
value={email}
onChange={(e) => setEmail(e.target.value)}
className="h-11"
/>
<Button onClick={changeEmail} className="w-full h-11">
2025-04-29 19:38:48 +02:00
{t('Settings.UserSettings.ChangeEmail.Button')}
2025-04-15 16:18:35 +02:00
</Button>
</div>
</div>
<div className="space-y-4">
<div className="border-b pb-2">
2025-04-29 19:38:48 +02:00
<h3 className="font-semibold text-lg">{t('Settings.UserSettings.ChangePassword.Title')}</h3>
2025-04-15 16:18:35 +02:00
</div>
{passwordErrorVisible && (
<Alert variant="destructive" className="animate-in fade-in-50">
<AlertCircle className="h-4 w-4" />
2025-04-29 19:38:48 +02:00
<AlertTitle>{t('Common.Error')}</AlertTitle>
2025-04-15 16:18:35 +02:00
<AlertDescription>{passwordError}</AlertDescription>
</Alert>
)}
{passwordSuccess && (
<Alert className="border-green-200 bg-green-50 text-green-800 dark:border-green-800 dark:bg-green-950 dark:text-green-300 animate-in fade-in-50">
<Check className="h-4 w-4" />
2025-04-29 19:38:48 +02:00
<AlertTitle>{t('Settings.UserSettings.ChangePassword.Success')}</AlertTitle>
2025-04-15 16:18:35 +02:00
</Alert>
)}
<div className="space-y-3">
<Input
type="password"
2025-04-29 19:38:48 +02:00
placeholder={t('Settings.UserSettings.ChangePassword.OldPassword')}
2025-04-15 16:18:35 +02:00
value={oldPassword}
onChange={(e) => setOldPassword(e.target.value)}
className="h-11"
/>
<Input
type="password"
2025-04-29 19:38:48 +02:00
placeholder={t('Settings.UserSettings.ChangePassword.NewPassword')}
2025-04-15 16:18:35 +02:00
value={password}
onChange={(e) => setPassword(e.target.value)}
className="h-11"
/>
<Input
type="password"
2025-04-29 19:38:48 +02:00
placeholder={t('Settings.UserSettings.ChangePassword.ConfirmPassword')}
2025-04-15 16:18:35 +02:00
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
className="h-11"
/>
<Button onClick={changePassword} className="w-full h-11">
2025-04-29 19:38:48 +02:00
{t('Settings.UserSettings.ChangePassword.Button')}
2025-04-15 16:18:35 +02:00
</Button>
</div>
</div>
</div>
</CardContent>
</Card>
<Card className="overflow-hidden border-2 border-muted/20 shadow-sm">
<CardHeader className="bg-muted/10 px-6 py-4 border-b">
<div className="flex items-center gap-2">
<Palette className="h-5 w-5 text-primary" />
2025-04-29 19:38:48 +02:00
<h2 className="text-xl font-semibold">{t('Settings.ThemeSettings.Title')}</h2>
2025-04-15 16:18:35 +02:00
</div>
</CardHeader>
<CardContent className="pb-6">
2025-04-15 16:18:35 +02:00
<div className="text-sm text-muted-foreground mb-6">
2025-04-29 19:38:48 +02:00
{t('Settings.ThemeSettings.Description')}
2025-04-15 16:18:35 +02:00
</div>
<div className="max-w-md">
<Select value={theme} onValueChange={(value: string) => setTheme(value)}>
<SelectTrigger className="w-full h-11">
<SelectValue>
2025-04-29 19:38:48 +02:00
{t(`Settings.ThemeSettings.${(theme ?? "system").charAt(0).toUpperCase() + (theme ?? "system").slice(1)}`)}
2025-04-15 16:18:35 +02:00
</SelectValue>
</SelectTrigger>
<SelectContent>
2025-04-29 19:38:48 +02:00
<SelectItem value="light">{t('Settings.ThemeSettings.Light')}</SelectItem>
<SelectItem value="dark">{t('Settings.ThemeSettings.Dark')}</SelectItem>
<SelectItem value="system">{t('Settings.ThemeSettings.System')}</SelectItem>
2025-04-15 16:18:35 +02:00
</SelectContent>
</Select>
</div>
</CardContent>
2025-04-12 20:01:43 +02:00
</Card>
2025-04-29 20:14:44 +02:00
<Card className="overflow-hidden border-2 border-muted/20 shadow-sm">
<CardHeader className="bg-muted/10 px-6 py-4 border-b">
<div className="flex items-center gap-2">
2025-04-29 20:27:21 +02:00
<Languages className="h-5 w-5 text-primary" />
2025-04-29 20:14:44 +02:00
<h2 className="text-xl font-semibold">{t('Settings.LanguageSettings.Title')}</h2>
</div>
</CardHeader>
<CardContent className="pb-6">
<div className="text-sm text-muted-foreground mb-6">
{t('Settings.LanguageSettings.Description')}
</div>
<div className="max-w-md">
<Select value={language} onValueChange={(value: string) => setLanguageFunc(value)}>
<SelectTrigger className="w-full h-11">
<SelectValue>
{t(`Settings.LanguageSettings.${(language ?? "english").charAt(0).toUpperCase() + (language ?? "english").slice(1)}`)}
</SelectValue>
</SelectTrigger>
<SelectContent>
<SelectItem value="english">{t('Settings.LanguageSettings.English')}</SelectItem>
<SelectItem value="german">{t('Settings.LanguageSettings.German')}</SelectItem>
</SelectContent>
</Select>
</div>
</CardContent>
</Card>
<Card className="overflow-hidden border-2 border-muted/20 shadow-sm">
<CardHeader className="bg-muted/10 px-6 py-4 border-b">
<div className="flex items-center gap-3">
<div className="bg-muted/20 p-2 rounded-full">
<Bell className="h-5 w-5 text-primary" />
</div>
2025-04-29 19:38:48 +02:00
<h2 className="text-xl font-semibold">{t('Settings.Notifications.Title')}</h2>
</div>
</CardHeader>
<CardContent className="p-6">
<div className="text-sm text-muted-foreground mb-6">
2025-04-29 19:38:48 +02:00
{t('Settings.Notifications.Description')}
</div>
<div className="grid gap-4 md:grid-cols-2">
<AlertDialog>
<AlertDialogTrigger asChild>
<Button className="w-full h-11 flex items-center gap-2">
2025-04-29 19:38:48 +02:00
{t('Settings.Notifications.AddChannel')}
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
2025-04-29 19:38:48 +02:00
<AlertDialogTitle>{t('Settings.Notifications.AddNotification.Title')}</AlertDialogTitle>
<AlertDialogDescription>
2025-04-27 14:56:40 +02:00
<div className="space-y-4">
<Input
type="text"
id="notificationName"
2025-04-29 19:38:48 +02:00
placeholder={t('Settings.Notifications.AddNotification.Name')}
2025-04-27 14:56:40 +02:00
onChange={(e) => setNotificationName(e.target.value)}
/>
<Select value={notificationType} onValueChange={(value: string) => setNotificationType(value)}>
<SelectTrigger className="w-full">
2025-04-29 19:38:48 +02:00
<SelectValue placeholder={t('Settings.Notifications.AddNotification.Type')} />
2025-04-27 14:56:40 +02:00
</SelectTrigger>
<SelectContent>
2025-04-29 19:38:48 +02:00
<SelectItem value="smtp">{t('Settings.Notifications.AddNotification.SMTP.Title')}</SelectItem>
<SelectItem value="telegram">{t('Settings.Notifications.AddNotification.Telegram.Title')}</SelectItem>
<SelectItem value="discord">{t('Settings.Notifications.AddNotification.Discord.Title')}</SelectItem>
<SelectItem value="gotify">{t('Settings.Notifications.AddNotification.Gotify.Title')}</SelectItem>
<SelectItem value="ntfy">{t('Settings.Notifications.AddNotification.Ntfy.Title')}</SelectItem>
<SelectItem value="pushover">{t('Settings.Notifications.AddNotification.Pushover.Title')}</SelectItem>
<SelectItem value="echobell">{t('Settings.Notifications.AddNotification.Echobell.Title')}</SelectItem>
2025-04-27 14:56:40 +02:00
</SelectContent>
{notificationType === "smtp" && (
<div className="mt-4 space-y-4">
<div className="grid md:grid-cols-2 gap-4">
<div className="space-y-1.5">
2025-04-29 19:38:48 +02:00
<Label>{t('Settings.Notifications.AddNotification.SMTP.Host')}</Label>
2025-04-27 14:56:40 +02:00
<Input
type="text"
placeholder="smtp.example.com"
onChange={(e) => setSmtpHost(e.target.value)}
/>
</div>
<div className="space-y-1.5">
2025-04-29 19:38:48 +02:00
<Label>{t('Settings.Notifications.AddNotification.SMTP.Port')}</Label>
2025-04-27 14:56:40 +02:00
<Input
type="number"
placeholder="587"
onChange={(e) => setSmtpPort(Number(e.target.value))}
/>
</div>
</div>
<div className="flex items-center space-x-2 pt-2 pb-4">
<Checkbox id="smtpSecure" onCheckedChange={(checked: any) => setSmtpSecure(checked)} />
<Label htmlFor="smtpSecure" className="text-sm font-medium leading-none">
2025-04-29 19:38:48 +02:00
{t('Settings.Notifications.AddNotification.SMTP.Secure')}
2025-04-27 14:56:40 +02:00
</Label>
</div>
<div className="grid gap-4">
<div className="space-y-1.5">
2025-04-29 19:38:48 +02:00
<Label>{t('Settings.Notifications.AddNotification.SMTP.User')}</Label>
2025-04-27 14:56:40 +02:00
<Input
type="text"
placeholder="user@example.com"
onChange={(e) => setSmtpUsername(e.target.value)}
/>
</div>
<div className="space-y-1.5">
2025-04-29 19:38:48 +02:00
<Label>{t('Settings.Notifications.AddNotification.SMTP.Pass')}</Label>
2025-04-27 14:56:40 +02:00
<Input
type="password"
placeholder="••••••••"
onChange={(e) => setSmtpPassword(e.target.value)}
/>
</div>
<div className="grid md:grid-cols-2 gap-4">
<div className="space-y-1.5">
2025-04-29 19:38:48 +02:00
<Label>{t('Settings.Notifications.AddNotification.SMTP.From')}</Label>
2025-04-27 14:56:40 +02:00
<Input
type="email"
placeholder="noreply@example.com"
onChange={(e) => setSmtpFrom(e.target.value)}
/>
</div>
<div className="space-y-1.5">
2025-04-29 19:38:48 +02:00
<Label>{t('Settings.Notifications.AddNotification.SMTP.To')}</Label>
2025-04-27 14:56:40 +02:00
<Input
type="email"
placeholder="admin@example.com"
onChange={(e) => setSmtpTo(e.target.value)}
/>
</div>
</div>
</div>
</div>
)}
{notificationType === "telegram" && (
<div className="mt-4 space-y-2">
<div className="grid w-full items-center gap-1.5">
2025-04-29 19:38:48 +02:00
<Label>{t('Settings.Notifications.AddNotification.Telegram.Token')}</Label>
<Input
type="text"
2025-04-27 14:56:40 +02:00
onChange={(e) => setTelegramToken(e.target.value)}
/>
</div>
2025-04-27 14:56:40 +02:00
<div className="grid w-full items-center gap-1.5">
2025-04-29 19:38:48 +02:00
<Label>{t('Settings.Notifications.AddNotification.Telegram.ChatId')}</Label>
<Input
2025-04-27 14:56:40 +02:00
type="text"
onChange={(e) => setTelegramChatId(e.target.value)}
/>
</div>
</div>
2025-04-27 14:56:40 +02:00
)}
2025-04-27 14:56:40 +02:00
{notificationType === "discord" && (
<div className="mt-4">
<div className="grid w-full items-center gap-1.5">
2025-04-29 19:38:48 +02:00
<Label>{t('Settings.Notifications.AddNotification.Discord.Webhook')}</Label>
<Input
type="text"
2025-04-27 14:56:40 +02:00
onChange={(e) => setDiscordWebhook(e.target.value)}
/>
</div>
2025-04-27 14:56:40 +02:00
</div>
)}
2025-04-27 14:56:40 +02:00
{notificationType === "gotify" && (
<div className="mt-4">
<div className="grid w-full items-center gap-1.5">
2025-04-29 19:38:48 +02:00
<Label>{t('Settings.Notifications.AddNotification.Gotify.Url')}</Label>
<Input
2025-04-27 14:56:40 +02:00
type="text"
onChange={(e) => setGotifyUrl(e.target.value)}
/>
2025-04-27 14:56:40 +02:00
<div className="grid w-full items-center gap-1.5">
2025-04-29 19:38:48 +02:00
<Label>{t('Settings.Notifications.AddNotification.Gotify.Token')}</Label>
<Input
2025-04-27 14:56:40 +02:00
type="text"
onChange={(e) => setGotifyToken(e.target.value)}
/>
</div>
</div>
</div>
2025-04-27 14:56:40 +02:00
)}
{notificationType === "ntfy" && (
<div className="mt-4">
<div className="grid w-full items-center gap-1.5">
2025-04-29 19:38:48 +02:00
<Label>{t('Settings.Notifications.AddNotification.Ntfy.Url')}</Label>
<Input
type="text"
2025-04-27 14:56:40 +02:00
onChange={(e) => setNtfyUrl(e.target.value)}
/>
2025-04-27 14:56:40 +02:00
<div className="grid w-full items-center gap-1.5">
2025-04-29 19:38:48 +02:00
<Label>{t('Settings.Notifications.AddNotification.Ntfy.Token')}</Label>
2025-04-27 14:56:40 +02:00
<Input
type="text"
onChange={(e) => setNtfyToken(e.target.value)}
/>
</div>
</div>
</div>
2025-04-27 14:56:40 +02:00
)}
{notificationType === "pushover" && (
<div className="mt-4 flex flex-col gap-2">
<div className="grid w-full items-center gap-1.5">
2025-04-29 19:38:48 +02:00
<Label>{t('Settings.Notifications.AddNotification.Pushover.Url')}</Label>
<Input
type="text"
2025-04-29 19:38:48 +02:00
placeholder={t('Settings.Notifications.AddNotification.Pushover.UrlPlaceholder')}
2025-04-27 14:56:40 +02:00
onChange={(e) => setPushoverUrl(e.target.value)}
/>
2025-04-27 14:56:40 +02:00
</div>
2025-04-21 15:31:38 +02:00
2025-04-27 14:56:40 +02:00
<div className="grid w-full items-center gap-1.5">
2025-04-29 19:38:48 +02:00
<Label>{t('Settings.Notifications.AddNotification.Pushover.Token')}</Label>
2025-04-27 14:56:40 +02:00
<Input
type="text"
2025-04-29 19:38:48 +02:00
placeholder={t('Settings.Notifications.AddNotification.Pushover.TokenPlaceholder')}
2025-04-27 14:56:40 +02:00
onChange={(e) => setPushoverToken(e.target.value)}
/>
</div>
2025-04-21 15:31:38 +02:00
2025-04-27 14:56:40 +02:00
<div className="grid w-full items-center gap-1.5">
2025-04-29 19:38:48 +02:00
<Label>{t('Settings.Notifications.AddNotification.Pushover.User')}</Label>
2025-04-27 14:56:40 +02:00
<Input
type="text"
2025-04-29 19:38:48 +02:00
placeholder={t('Settings.Notifications.AddNotification.Pushover.UserPlaceholder')}
2025-04-27 14:56:40 +02:00
onChange={(e) => setPushoverUser(e.target.value)}
/>
</div>
2025-04-21 15:31:38 +02:00
</div>
2025-04-27 14:56:40 +02:00
)}
{notificationType === "echobell" && (
<div className="mt-4 flex flex-col gap-2">
<div className="grid w-full items-center gap-1.5">
<Label>{t('Settings.Notifications.AddNotification.Echobell.Url')}</Label>
<Input
type="text"
placeholder="e.g. https://hook.echobell.one/t/xxx"
onChange={(e) => setEchobellURL(e.target.value)}
/>
</div>
2025-04-29 22:54:32 +02:00
<span className="text-xs text-muted-foreground">{t('Settings.Notifications.AddNotification.Echobell.AddMessage')}</span>
</div>
)}
2025-04-27 14:56:40 +02:00
</Select>
</div>
</AlertDialogDescription>
<AlertDialogFooter>
2025-04-29 19:38:48 +02:00
<AlertDialogCancel>{t('Common.cancel')}</AlertDialogCancel>
<AlertDialogAction onClick={addNotification}>{t('Common.add')}</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
<AlertDialog>
<AlertDialogTrigger asChild>
<Button className="w-full h-11" variant="outline">
2025-04-29 19:38:48 +02:00
{t('Settings.Notifications.CustomizeText.Display')}
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
2025-04-29 19:38:48 +02:00
<AlertDialogTitle>{t('Settings.Notifications.CustomizeText.Title')}</AlertDialogTitle>
<AlertDialogDescription>
<div className="space-y-4">
2025-04-17 17:27:01 +02:00
<div className="space-y-1.5">
2025-04-29 19:38:48 +02:00
<Label>{t('Settings.Notifications.CustomizeText.Application')}</Label>
<Textarea
value={notificationTextApplication}
onChange={(e) => setNotificationTextApplication(e.target.value)}
rows={4}
/>
</div>
<div className="space-y-1.5">
2025-04-29 19:38:48 +02:00
<Label>{t('Settings.Notifications.CustomizeText.Server')}</Label>
<Textarea
value={notificationTextServer}
onChange={(e) => setNotificationTextServer(e.target.value)}
rows={4}
/>
</div>
2025-04-29 19:38:48 +02:00
<div className="pt-4 text-sm text-muted-foreground">
{t('Settings.Notifications.CustomizeText.Placeholders.Title')}
<ul className="list-disc list-inside space-y-1 pt-2">
<li>
<b>{t('Settings.Notifications.CustomizeText.Placeholders.Server.Title')}</b>
<ul className="list-disc list-inside ml-4 space-y-1 pt-1 text-muted-foreground">
2025-04-29 19:38:48 +02:00
<li>{t('Settings.Notifications.CustomizeText.Placeholders.Server.Name')}</li>
<li>{t('Settings.Notifications.CustomizeText.Placeholders.Server.Status')}</li>
</ul>
2025-04-29 19:38:48 +02:00
</li>
<li>
<b>{t('Settings.Notifications.CustomizeText.Placeholders.Application.Title')}</b>
<ul className="list-disc list-inside ml-4 space-y-1 pt-1 text-muted-foreground">
2025-04-29 19:38:48 +02:00
<li>{t('Settings.Notifications.CustomizeText.Placeholders.Application.Name')}</li>
<li>{t('Settings.Notifications.CustomizeText.Placeholders.Application.Url')}</li>
<li>{t('Settings.Notifications.CustomizeText.Placeholders.Application.Status')}</li>
</ul>
2025-04-29 19:38:48 +02:00
</li>
</ul>
</div>
</div>
</AlertDialogDescription>
<AlertDialogFooter>
2025-04-29 19:38:48 +02:00
<AlertDialogCancel>{t('Common.cancel')}</AlertDialogCancel>
<AlertDialogAction onClick={editNotificationText}>
{t('Common.Save')}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
<div className="mt-8">
2025-04-29 19:38:48 +02:00
<h3 className="text-lg font-medium mb-4">{t('Settings.Notifications.ActiveChannels')}</h3>
<div className="space-y-3">
{notifications.length > 0 ? (
notifications.map((notification) => (
<div
key={notification.id}
className="flex items-center justify-between p-4 rounded-lg border bg-card transition-all hover:shadow-sm"
>
<div className="flex items-center gap-3">
{notification.type === "smtp" && (
<div className="bg-muted/20 p-2 rounded-full">
<AtSign className="h-5 w-5 text-primary" />
</div>
)}
{notification.type === "telegram" && (
<div className="bg-muted/20 p-2 rounded-full">
<Send className="h-5 w-5 text-primary" />
</div>
)}
{notification.type === "discord" && (
<div className="bg-muted/20 p-2 rounded-full">
<MessageSquare className="h-5 w-5 text-primary" />
</div>
)}
{notification.type === "gotify" && (
<div className="bg-muted/20 p-2 rounded-full">
<Bell className="h-5 w-5 text-primary" />
</div>
)}
{notification.type === "ntfy" && (
<div className="bg-muted/20 p-2 rounded-full">
<Bell className="h-5 w-5 text-primary" />
</div>
)}
2025-04-21 15:31:38 +02:00
{notification.type === "pushover" && (
<div className="bg-muted/20 p-2 rounded-full">
<Bell className="h-5 w-5 text-primary" />
</div>
)}
<div className="space-y-1">
2025-04-29 19:38:48 +02:00
<h3 className="font-medium capitalize">
{notification.name ||
t(`Settings.Notifications.AddNotification.${notification.type.charAt(0).toUpperCase() + notification.type.slice(1)}.Title`)}
</h3>
<p className="text-xs text-muted-foreground">
2025-04-29 19:38:48 +02:00
{t(`Settings.Notifications.AddNotification.${notification.type.charAt(0).toUpperCase() + notification.type.slice(1)}.Description`)}
</p>
</div>
</div>
2025-04-23 21:34:52 +02:00
<div className="flex items-center gap-2">
<Button
variant="outline"
size="sm"
className="hover:bg-muted/20"
onClick={() => testNotification(notification.id)}
>
<Play className="h-4 w-4 mr-1" />
2025-04-29 19:38:48 +02:00
{t('Settings.Notifications.Test')}
2025-04-23 21:34:52 +02:00
</Button>
<Button
variant="ghost"
size="sm"
className="hover:bg-muted/20"
onClick={() => deleteNotification(notification.id)}
>
<Trash2 className="h-4 w-4 mr-1" />
2025-04-29 19:38:48 +02:00
{t('Settings.Notifications.Delete')}
2025-04-23 21:34:52 +02:00
</Button>
</div>
</div>
))
) : (
<div className="text-center py-12 border rounded-lg bg-muted/5">
<div className="flex justify-center mb-3">
<div className="bg-muted/20 p-3 rounded-full">
<Bell className="h-6 w-6 text-muted-foreground" />
</div>
</div>
2025-04-29 19:38:48 +02:00
<h3 className="text-lg font-medium mb-1">
{t('Settings.Notifications.NoNotifications')}
</h3>
<p className="text-sm text-muted-foreground max-w-md mx-auto">
2025-04-29 19:38:48 +02:00
{t('Settings.Notifications.NoNotificationsDescription')}
</p>
</div>
)}
</div>
</div>
2025-04-23 21:34:52 +02:00
<Toaster />
</CardContent>
</Card>
2025-04-12 20:01:43 +02:00
</div>
</div>
</SidebarInset>
</SidebarProvider>
2025-04-15 16:18:35 +02:00
)
2025-04-29 19:38:48 +02:00
}