CoreControl/components/cards/settings/NotificationSettings.tsx

577 lines
26 KiB
TypeScript
Raw Normal View History

2025-05-26 12:26:39 +02:00
"use client"
2025-05-26 11:30:19 +02:00
2025-05-26 12:26:39 +02:00
import { useEffect, useState } from "react"
import useNotifications from "@/hooks/useNotifications"
2025-05-26 15:29:20 +02:00
import { Check, CircleHelp, Copy, Server, Smartphone } from "lucide-react"
2025-05-26 11:30:19 +02:00
interface NotificationSettingsProps {
2025-05-26 12:26:39 +02:00
onError: (message: string) => void
onSuccess: (message: string) => void
2025-05-26 11:30:19 +02:00
}
export const NotificationSettings = ({ onError, onSuccess }: NotificationSettingsProps) => {
2025-05-26 12:26:39 +02:00
const {
getNotificationApplicationsSettings,
getNotificationServerSettings,
editNotificationApplicationsSettings,
editNotificationServerSettings,
} = useNotifications()
const [applicationsSettings, setApplicationsSettings] = useState<any>(null)
const [serverSettings, setServerSettings] = useState<any>(null)
2025-05-26 15:29:20 +02:00
const [copiedVariable, setCopiedVariable] = useState<string | null>(null)
const [serverCollapsed, setServerCollapsed] = useState(true)
const [applicationCollapsed, setApplicationCollapsed] = useState(true)
2025-05-26 12:26:39 +02:00
const fetchNotificationSettings = async () => {
const applicationsSettings = await getNotificationApplicationsSettings()
const serverSettings = await getNotificationServerSettings()
setServerSettings(
serverSettings || {
enabled: false,
2025-05-26 15:29:20 +02:00
statusChange: true,
cpuLimit: 80,
gpuLimit: 80,
memoryLimit: 80,
diskLimit: 80,
temperatureLimit: 80,
notificationTextStatus: "{servername} went {status}",
notificationTextCpu: "{servername} CPU usage is high at {cpu}%",
notificationTextGpu: "{servername} GPU usage is high at {gpu}%",
notificationTextMemory: "{servername} Memory usage is high at {memory}%",
notificationTextDisk: "{servername} Disk usage is high at {disk}%",
notificationTextTemperature: "{servername} Temperature is high at {temperature}°C",
notificationCpu: true,
notificationGpu: true,
notificationMemory: true,
notificationDisk: true,
notificationTemperature: true,
2025-05-26 12:26:39 +02:00
},
)
setApplicationsSettings(
applicationsSettings || {
enabled: false,
statusChange: false,
2025-05-26 15:29:20 +02:00
latencyLimit: 1000,
notificationTextStatus: "{appname} went {status}",
notificationTextLatency: "{appname} latency is high at {latency}ms",
notificationLatency: true,
2025-05-26 12:26:39 +02:00
},
)
}
useEffect(() => {
fetchNotificationSettings()
}, [])
const saveNotificationSettings = async () => {
2025-05-26 15:29:20 +02:00
const response = editNotificationServerSettings(serverSettings)
if (typeof response === "string") {
onError && onError(response)
return
}
const response2 = editNotificationApplicationsSettings(applicationsSettings)
if (typeof response2 === "string") {
onError && onError(response2)
return
}
try {
const successMessage = await response
const successMessage2 = await response2
console.log(successMessage, successMessage2)
if (onSuccess && successMessage && successMessage2) {
onSuccess("Notification settings saved successfully")
}
} catch (apiError: any) {
onError && onError(apiError)
}
}
const copyToClipboard = async (text: string) => {
try {
await navigator.clipboard.writeText(text)
setCopiedVariable(text)
setTimeout(() => setCopiedVariable(null), 2000)
} catch (err) {
console.error("Failed to copy text: ", err)
}
2025-05-26 12:26:39 +02:00
}
const ResourceThresholdRow = ({
label,
checked,
onCheckedChange,
value,
onValueChange,
unit,
message,
onMessageChange,
disabled,
}: {
label: string
checked: boolean
onCheckedChange: () => void
value: number
onValueChange: (value: string) => void
unit: string
message: string
onMessageChange: (value: string) => void
disabled: boolean
}) => (
2025-05-26 15:29:20 +02:00
<div className="card bg-base-100 shadow-sm border border-base-300 mb-3">
2025-05-26 12:26:39 +02:00
<div className="card-body p-4">
2025-05-26 15:29:20 +02:00
<div className="space-y-4">
{/* Header Row - Checkbox and Threshold */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<input
type="checkbox"
className="checkbox checkbox-primary"
checked={checked}
onChange={onCheckedChange}
disabled={disabled}
/>
<span className="font-medium">{label}</span>
</div>
2025-05-26 12:26:39 +02:00
2025-05-26 15:29:20 +02:00
<div className="flex items-center gap-2">
<span className="text-sm text-base-content/70">Threshold:</span>
<div className="flex items-center gap-1">
<input
type="number"
className="input input-bordered input-sm w-20 text-center"
value={value}
onChange={(e) => onValueChange(e.target.value)}
disabled={disabled}
min="0"
max={unit === "°C" ? "200" : "100"}
/>
<span className="text-sm font-medium text-base-content/80">{unit}</span>
</div>
</div>
2025-05-26 12:26:39 +02:00
</div>
2025-05-26 15:29:20 +02:00
{/* Message Template Row */}
<div className="space-y-2">
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-base-content/80">Notification Message:</span>
<div
className="tooltip tooltip-right"
data-tip="Use template variables like {servername}, {status}, etc."
>
<CircleHelp className="w-4 h-4 text-base-content/50" />
</div>
</div>
2025-05-26 12:26:39 +02:00
<input
type="text"
2025-05-26 15:29:20 +02:00
className="input input-bordered input-sm w-full"
placeholder={`Enter ${label.toLowerCase()} notification message...`}
2025-05-26 12:26:39 +02:00
value={message}
onChange={(e) => onMessageChange(e.target.value)}
disabled={disabled}
/>
</div>
</div>
</div>
</div>
)
return (
2025-05-26 15:29:20 +02:00
<div className="w-full bg-base-200 p-6 rounded-2xl border border-stone-800">
{/* Header */}
<div className="text-center lg:text-left mb-6">
<h2 className="text-xl font-bold">Notification Settings</h2>
<p className="text-sm opacity-70 mt-1">Configure monitoring alerts for your servers and applications</p>
</div>
2025-05-26 12:26:39 +02:00
2025-05-26 15:29:20 +02:00
<div className="space-y-8">
{/* Server Monitoring Section */}
<div className="card bg-base-100 shadow-lg border border-base-300">
<div className={`card-body p-6 ${serverCollapsed ? "pb-2" : ""}`}>
<div
className={`flex items-center gap-3 ${serverCollapsed ? "mb-2" : "mb-6"} cursor-pointer hover:bg-base-200 -m-2 p-2 rounded-lg transition-colors`}
onClick={() => setServerCollapsed(!serverCollapsed)}
>
<div className="p-2 bg-primary/10 rounded-lg">
<Server className="w-6 h-6 text-primary" />
</div>
<div className="flex-1">
2025-05-28 12:14:38 +02:00
<h3 className="text-lg font-bold">Server Notifications</h3>
2025-05-26 15:29:20 +02:00
<p className="text-sm opacity-70">Monitor server resources and performance</p>
2025-05-26 12:26:39 +02:00
</div>
2025-05-26 15:29:20 +02:00
<span
2025-05-26 15:34:38 +02:00
className={`px-3 py-1 rounded-full text-xs font-medium w-20 text-center ${
2025-05-26 15:29:20 +02:00
serverSettings?.enabled ? "bg-success text-white" : "bg-error text-white"
}`}
>
{serverSettings?.enabled ? "Active" : "Inactive"}
</span>
<div className={`transition-transform duration-200 ${serverCollapsed ? "" : "rotate-180"}`}>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</div>
</div>
2025-05-26 12:26:39 +02:00
2025-05-26 15:29:20 +02:00
<div
className={`transition-all duration-300 overflow-hidden ${serverCollapsed ? "max-h-0 opacity-0" : "max-h-none opacity-100"}`}
>
2025-05-26 12:26:39 +02:00
{/* Master Toggle */}
<div className="card bg-base-200 mb-6">
<div className="card-body p-4">
2025-05-26 15:29:20 +02:00
<div className="flex items-center gap-3">
<input
type="checkbox"
className="toggle toggle-primary"
checked={serverSettings?.enabled}
onChange={() => setServerSettings({ ...serverSettings, enabled: !serverSettings.enabled })}
/>
<div>
<h4 className="font-semibold">Enable Server Monitoring</h4>
<p className="text-sm text-base-content/70">Turn on global server monitoring notifications</p>
2025-05-26 12:26:39 +02:00
</div>
</div>
</div>
</div>
2025-05-26 15:29:20 +02:00
{/* Settings Content */}
<div className={`space-y-6 ${!serverSettings?.enabled ? "opacity-50" : ""}`}>
{/* Status Change Alerts */}
2025-05-26 12:26:39 +02:00
<div className="card bg-base-200">
<div className="card-body p-4">
<div className="flex flex-col lg:flex-row lg:items-center gap-4">
<div className="flex items-center gap-3 lg:flex-1">
<input
type="checkbox"
className="checkbox checkbox-primary"
checked={serverSettings?.statusChange}
onChange={() =>
setServerSettings({ ...serverSettings, statusChange: !serverSettings.statusChange })
}
disabled={!serverSettings?.enabled}
/>
<div>
<span className="font-medium">Status Change Alerts</span>
<p className="text-xs text-base-content/70">Get notified when server status changes</p>
</div>
</div>
<div className="flex items-center gap-2 lg:flex-1">
<input
type="text"
className="input input-bordered input-sm flex-1"
placeholder="Status change notification message..."
value={serverSettings?.notificationTextStatus}
onChange={(e) =>
setServerSettings({ ...serverSettings, notificationTextStatus: e.target.value })
}
disabled={!serverSettings?.enabled}
/>
2025-05-26 15:29:20 +02:00
<div className="tooltip tooltip-left" data-tip="Notification message template">
2025-05-26 12:26:39 +02:00
<CircleHelp className="w-4 h-4 text-base-content/50" />
</div>
</div>
</div>
</div>
</div>
{/* Resource Thresholds */}
2025-05-26 15:29:20 +02:00
<div>
<h4 className="text-base font-semibold mb-4">Resource Thresholds</h4>
<div className="space-y-0">
2025-05-26 12:26:39 +02:00
<ResourceThresholdRow
label="CPU Usage"
checked={serverSettings?.notificationCpu}
onCheckedChange={() =>
setServerSettings({ ...serverSettings, notificationCpu: !serverSettings.notificationCpu })
}
value={serverSettings?.cpuLimit}
onValueChange={(value) => setServerSettings({ ...serverSettings, cpuLimit: value })}
unit="%"
message={serverSettings?.notificationTextCpu}
onMessageChange={(value) => setServerSettings({ ...serverSettings, notificationTextCpu: value })}
disabled={!serverSettings?.enabled}
/>
<ResourceThresholdRow
label="GPU Usage"
checked={serverSettings?.notificationGpu}
onCheckedChange={() =>
setServerSettings({ ...serverSettings, notificationGpu: !serverSettings.notificationGpu })
}
value={serverSettings?.gpuLimit}
onValueChange={(value) => setServerSettings({ ...serverSettings, gpuLimit: value })}
unit="%"
message={serverSettings?.notificationTextGpu}
onMessageChange={(value) => setServerSettings({ ...serverSettings, notificationTextGpu: value })}
disabled={!serverSettings?.enabled}
/>
<ResourceThresholdRow
label="Memory Usage"
checked={serverSettings?.notificationMemory}
onCheckedChange={() =>
setServerSettings({ ...serverSettings, notificationMemory: !serverSettings.notificationMemory })
}
value={serverSettings?.memoryLimit}
onValueChange={(value) => setServerSettings({ ...serverSettings, memoryLimit: value })}
2025-05-26 15:29:20 +02:00
unit="%"
2025-05-26 12:26:39 +02:00
message={serverSettings?.notificationTextMemory}
2025-05-26 15:29:20 +02:00
onMessageChange={(value) =>
setServerSettings({ ...serverSettings, notificationTextMemory: value })
}
2025-05-26 12:26:39 +02:00
disabled={!serverSettings?.enabled}
/>
<ResourceThresholdRow
label="Disk Usage"
checked={serverSettings?.notificationDisk}
onCheckedChange={() =>
setServerSettings({ ...serverSettings, notificationDisk: !serverSettings.notificationDisk })
}
value={serverSettings?.diskLimit}
onValueChange={(value) => setServerSettings({ ...serverSettings, diskLimit: value })}
unit="%"
message={serverSettings?.notificationTextDisk}
onMessageChange={(value) => setServerSettings({ ...serverSettings, notificationTextDisk: value })}
disabled={!serverSettings?.enabled}
/>
<ResourceThresholdRow
label="Temperature"
checked={serverSettings?.notificationTemperature}
onCheckedChange={() =>
setServerSettings({
...serverSettings,
notificationTemperature: !serverSettings.notificationTemperature,
})
}
value={serverSettings?.temperatureLimit}
onValueChange={(value) => setServerSettings({ ...serverSettings, temperatureLimit: value })}
unit="°C"
message={serverSettings?.notificationTextTemperature}
onMessageChange={(value) =>
setServerSettings({ ...serverSettings, notificationTextTemperature: value })
}
disabled={!serverSettings?.enabled}
/>
</div>
</div>
2025-05-26 15:29:20 +02:00
{/* Server Variables Reference */}
<div className="card bg-gradient-to-br from-base-200 to-base-300 border border-base-300 mt-6">
<div className="card-body p-4">
<div className="flex items-center gap-2 mb-3">
<CircleHelp className="w-4 h-4 text-primary" />
<h5 className="text-sm font-semibold">Available Server Variables</h5>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-2">
{[
{ var: "{servername}", desc: "Server name or hostname" },
{ var: "{status}", desc: "Current server status" },
{ var: "{cpu}", desc: "CPU usage percentage" },
{ var: "{gpu}", desc: "GPU usage percentage" },
{ var: "{memory}", desc: "Memory usage percentage" },
{ var: "{disk}", desc: "Disk usage percentage" },
{ var: "{temperature}", desc: "Temperature in Celsius" },
].map((item) => (
<div
key={item.var}
className="group bg-base-100 border border-base-300 rounded-lg p-2 hover:border-primary hover:shadow-sm transition-all duration-200 cursor-pointer"
onClick={() => copyToClipboard(item.var)}
>
<div className="flex items-center justify-between">
<div className="flex-1">
<div className="font-mono text-xs font-semibold text-primary">{item.var}</div>
<div className="text-xs text-base-content/70">{item.desc}</div>
</div>
<div className="ml-2 opacity-0 group-hover:opacity-100 transition-opacity">
{copiedVariable === item.var ? (
<Check className="w-3 h-3 text-success" />
) : (
<Copy className="w-3 h-3 text-base-content/50" />
)}
</div>
</div>
</div>
))}
</div>
</div>
</div>
2025-05-26 12:26:39 +02:00
</div>
</div>
</div>
2025-05-26 15:29:20 +02:00
</div>
2025-05-26 12:26:39 +02:00
2025-05-26 15:29:20 +02:00
{/* Application Monitoring Section */}
<div className="card bg-base-100 shadow-lg border border-base-300">
<div className={`card-body p-6 ${applicationCollapsed ? "pb-2" : ""}`}>
<div
className={`flex items-center gap-3 ${applicationCollapsed ? "mb-2" : "mb-6"} cursor-pointer hover:bg-base-200 -m-2 p-2 rounded-lg transition-colors`}
onClick={() => setApplicationCollapsed(!applicationCollapsed)}
>
<div className="p-2 bg-secondary/10 rounded-lg">
<Smartphone className="w-6 h-6 text-secondary" />
2025-05-26 12:26:39 +02:00
</div>
2025-05-26 15:29:20 +02:00
<div className="flex-1">
2025-05-28 12:14:38 +02:00
<h3 className="text-lg font-bold">Application Notifications</h3>
2025-05-26 15:29:20 +02:00
<p className="text-sm opacity-70">Configure application monitoring notifications</p>
</div>
<span
2025-05-26 15:34:38 +02:00
className={`px-3 py-1 rounded-full text-xs font-medium w-20 text-center ${
2025-05-26 15:29:20 +02:00
applicationsSettings?.enabled ? "bg-success text-white" : "bg-error text-white"
}`}
>
{applicationsSettings?.enabled ? "Active" : "Inactive"}
</span>
<div className={`transition-transform duration-200 ${applicationCollapsed ? "" : "rotate-180"}`}>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</div>
</div>
2025-05-26 12:26:39 +02:00
2025-05-26 15:29:20 +02:00
<div
className={`transition-all duration-300 overflow-hidden ${applicationCollapsed ? "max-h-0 opacity-0" : "max-h-none opacity-100"}`}
>
2025-05-26 12:26:39 +02:00
{/* Master Toggle */}
<div className="card bg-base-200 mb-6">
<div className="card-body p-4">
2025-05-26 15:29:20 +02:00
<div className="flex items-center gap-3">
<input
type="checkbox"
2025-05-28 12:14:38 +02:00
className="toggle toggle-primary"
2025-05-26 15:29:20 +02:00
checked={applicationsSettings?.enabled}
onChange={() =>
setApplicationsSettings({ ...applicationsSettings, enabled: !applicationsSettings.enabled })
}
/>
<div>
<h4 className="font-semibold">Enable Application Monitoring</h4>
<p className="text-sm text-base-content/70">Turn on global application monitoring</p>
2025-05-26 12:26:39 +02:00
</div>
</div>
</div>
</div>
{/* Application Settings */}
2025-05-26 15:29:20 +02:00
<div className={`space-y-6 ${!applicationsSettings?.enabled ? "opacity-50" : ""}`}>
2025-05-26 12:26:39 +02:00
{/* Status Change Alerts */}
<div className="card bg-base-200">
<div className="card-body p-4">
<div className="flex flex-col lg:flex-row lg:items-center gap-4">
<div className="flex items-center gap-3 lg:flex-1">
<input
type="checkbox"
2025-05-28 12:14:38 +02:00
className="checkbox checkbox-primary"
2025-05-26 12:26:39 +02:00
checked={applicationsSettings?.statusChange}
onChange={() =>
setApplicationsSettings({
...applicationsSettings,
statusChange: !applicationsSettings.statusChange,
})
}
disabled={!applicationsSettings?.enabled}
/>
<div>
<span className="font-medium">Status Change Alerts</span>
<p className="text-xs text-base-content/70">Get notified when application status changes</p>
</div>
</div>
<div className="flex items-center gap-2 lg:flex-1">
<input
type="text"
className="input input-bordered input-sm flex-1"
placeholder="Status change notification message..."
value={applicationsSettings?.notificationTextStatus}
onChange={(e) =>
setApplicationsSettings({ ...applicationsSettings, notificationTextStatus: e.target.value })
}
disabled={!applicationsSettings?.enabled}
/>
2025-05-26 15:29:20 +02:00
<div className="tooltip tooltip-left" data-tip="Notification message template">
2025-05-26 12:26:39 +02:00
<CircleHelp className="w-4 h-4 text-base-content/50" />
</div>
</div>
2025-05-26 11:30:19 +02:00
</div>
2025-05-26 12:26:39 +02:00
</div>
2025-05-26 11:30:19 +02:00
</div>
2025-05-26 12:26:39 +02:00
2025-05-26 15:29:20 +02:00
{/* Performance Thresholds */}
<div>
<h4 className="text-base font-semibold mb-4">Performance Thresholds</h4>
2025-05-26 12:26:39 +02:00
<ResourceThresholdRow
label="Latency Threshold"
checked={applicationsSettings?.notificationLatency}
onCheckedChange={() =>
setApplicationsSettings({
...applicationsSettings,
notificationLatency: !applicationsSettings.notificationLatency,
})
}
value={applicationsSettings?.latencyLimit}
2025-05-26 15:29:20 +02:00
onValueChange={(value) =>
setApplicationsSettings({ ...applicationsSettings, latencyLimit: Number(value) })
}
2025-05-26 12:26:39 +02:00
unit="ms"
message={applicationsSettings?.notificationTextLatency}
onMessageChange={(value) =>
setApplicationsSettings({ ...applicationsSettings, notificationTextLatency: value })
}
disabled={!applicationsSettings?.enabled}
/>
</div>
2025-05-26 15:29:20 +02:00
{/* Application Variables Reference */}
<div className="card bg-gradient-to-br from-base-200 to-base-300 border border-base-300 mt-6">
<div className="card-body p-4">
<div className="flex items-center gap-2 mb-3">
<CircleHelp className="w-4 h-4 text-secondary" />
<h5 className="text-sm font-semibold">Available Application Variables</h5>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
{[
{ var: "{appname}", desc: "Application name" },
{ var: "{status}", desc: "Application status" },
{ var: "{latency}", desc: "Application latency in ms" },
].map((item) => (
<div
key={item.var}
className="group bg-base-100 border border-base-300 rounded-lg p-2 hover:border-primary hover:shadow-sm transition-all duration-200 cursor-pointer"
onClick={() => copyToClipboard(item.var)}
>
<div className="flex items-center justify-between">
<div className="flex-1">
<div className="font-mono text-xs font-semibold text-secondary">{item.var}</div>
<div className="text-xs text-base-content/70">{item.desc}</div>
</div>
<div className="ml-2 opacity-0 group-hover:opacity-100 transition-opacity">
{copiedVariable === item.var ? (
<Check className="w-3 h-3 text-success" />
) : (
<Copy className="w-3 h-3 text-base-content/50" />
)}
</div>
</div>
</div>
))}
</div>
</div>
</div>
2025-05-26 12:26:39 +02:00
</div>
2025-05-26 11:30:19 +02:00
</div>
2025-05-26 12:26:39 +02:00
</div>
2025-05-26 15:29:20 +02:00
</div>
2025-05-26 12:26:39 +02:00
2025-05-26 15:29:20 +02:00
{/* Action Buttons */}
<div className="flex justify-end">
<button className="btn btn-primary" onClick={saveNotificationSettings}>
Save Settings
</button>
2025-05-26 11:30:19 +02:00
</div>
2025-05-26 12:26:39 +02:00
</div>
</div>
)
}