mirror of
https://github.com/crocofied/CoreControl.git
synced 2025-12-17 15:36:50 +00:00
Finisch Notification Settings System
This commit is contained in:
parent
61a9442443
commit
22c7ce29e3
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
import useNotifications from "@/hooks/useNotifications"
|
import useNotifications from "@/hooks/useNotifications"
|
||||||
import { CircleHelp, Server, Smartphone } from "lucide-react"
|
import { Check, CircleHelp, Copy, Server, Smartphone } from "lucide-react"
|
||||||
|
|
||||||
interface NotificationSettingsProps {
|
interface NotificationSettingsProps {
|
||||||
onError: (message: string) => void
|
onError: (message: string) => void
|
||||||
@ -18,6 +18,9 @@ export const NotificationSettings = ({ onError, onSuccess }: NotificationSetting
|
|||||||
} = useNotifications()
|
} = useNotifications()
|
||||||
const [applicationsSettings, setApplicationsSettings] = useState<any>(null)
|
const [applicationsSettings, setApplicationsSettings] = useState<any>(null)
|
||||||
const [serverSettings, setServerSettings] = useState<any>(null)
|
const [serverSettings, setServerSettings] = useState<any>(null)
|
||||||
|
const [copiedVariable, setCopiedVariable] = useState<string | null>(null)
|
||||||
|
const [serverCollapsed, setServerCollapsed] = useState(true)
|
||||||
|
const [applicationCollapsed, setApplicationCollapsed] = useState(true)
|
||||||
|
|
||||||
const fetchNotificationSettings = async () => {
|
const fetchNotificationSettings = async () => {
|
||||||
const applicationsSettings = await getNotificationApplicationsSettings()
|
const applicationsSettings = await getNotificationApplicationsSettings()
|
||||||
@ -26,23 +29,23 @@ export const NotificationSettings = ({ onError, onSuccess }: NotificationSetting
|
|||||||
setServerSettings(
|
setServerSettings(
|
||||||
serverSettings || {
|
serverSettings || {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
statusChange: false,
|
statusChange: true,
|
||||||
cpuLimit: 0,
|
cpuLimit: 80,
|
||||||
gpuLimit: 0,
|
gpuLimit: 80,
|
||||||
memoryLimit: 0,
|
memoryLimit: 80,
|
||||||
diskLimit: 0,
|
diskLimit: 80,
|
||||||
temperatureLimit: 0,
|
temperatureLimit: 80,
|
||||||
notificationTextStatus: "",
|
notificationTextStatus: "{servername} went {status}",
|
||||||
notificationTextCpu: "",
|
notificationTextCpu: "{servername} CPU usage is high at {cpu}%",
|
||||||
notificationTextGpu: "",
|
notificationTextGpu: "{servername} GPU usage is high at {gpu}%",
|
||||||
notificationTextMemory: "",
|
notificationTextMemory: "{servername} Memory usage is high at {memory}%",
|
||||||
notificationTextDisk: "",
|
notificationTextDisk: "{servername} Disk usage is high at {disk}%",
|
||||||
notificationTextTemperature: "",
|
notificationTextTemperature: "{servername} Temperature is high at {temperature}°C",
|
||||||
notificationCpu: false,
|
notificationCpu: true,
|
||||||
notificationGpu: false,
|
notificationGpu: true,
|
||||||
notificationMemory: false,
|
notificationMemory: true,
|
||||||
notificationDisk: false,
|
notificationDisk: true,
|
||||||
notificationTemperature: false,
|
notificationTemperature: true,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -50,10 +53,10 @@ export const NotificationSettings = ({ onError, onSuccess }: NotificationSetting
|
|||||||
applicationsSettings || {
|
applicationsSettings || {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
statusChange: false,
|
statusChange: false,
|
||||||
latencyLimit: 0,
|
latencyLimit: 1000,
|
||||||
notificationTextStatus: "",
|
notificationTextStatus: "{appname} went {status}",
|
||||||
notificationTextLatency: "",
|
notificationTextLatency: "{appname} latency is high at {latency}ms",
|
||||||
notificationLatency: false,
|
notificationLatency: true,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -63,8 +66,37 @@ export const NotificationSettings = ({ onError, onSuccess }: NotificationSetting
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const saveNotificationSettings = async () => {
|
const saveNotificationSettings = async () => {
|
||||||
await editNotificationServerSettings(serverSettings)
|
const response = editNotificationServerSettings(serverSettings)
|
||||||
await editNotificationApplicationsSettings(applicationsSettings)
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ResourceThresholdRow = ({
|
const ResourceThresholdRow = ({
|
||||||
@ -88,43 +120,58 @@ export const NotificationSettings = ({ onError, onSuccess }: NotificationSetting
|
|||||||
onMessageChange: (value: string) => void
|
onMessageChange: (value: string) => void
|
||||||
disabled: boolean
|
disabled: boolean
|
||||||
}) => (
|
}) => (
|
||||||
<div className="card bg-base-100 shadow-sm border border-base-300">
|
<div className="card bg-base-100 shadow-sm border border-base-300 mb-3">
|
||||||
<div className="card-body p-4">
|
<div className="card-body p-4">
|
||||||
<div className="flex flex-col lg:flex-row lg:items-center gap-4">
|
<div className="space-y-4">
|
||||||
<div className="flex items-center gap-3 lg:w-48">
|
{/* Header Row - Checkbox and Threshold */}
|
||||||
<input
|
<div className="flex items-center justify-between">
|
||||||
type="checkbox"
|
<div className="flex items-center gap-3">
|
||||||
className="checkbox checkbox-primary checkbox-sm"
|
<input
|
||||||
checked={checked}
|
type="checkbox"
|
||||||
onChange={onCheckedChange}
|
className="checkbox checkbox-primary"
|
||||||
disabled={disabled}
|
checked={checked}
|
||||||
/>
|
onChange={onCheckedChange}
|
||||||
<span className="font-medium text-sm">{label}</span>
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
<span className="font-medium">{label}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2 lg:w-32">
|
{/* Message Template Row */}
|
||||||
<input
|
<div className="space-y-2">
|
||||||
type="number"
|
<div className="flex items-center gap-2">
|
||||||
className="input input-bordered input-sm w-20"
|
<span className="text-sm font-medium text-base-content/80">Notification Message:</span>
|
||||||
value={value}
|
<div
|
||||||
onChange={(e) => onValueChange(e.target.value)}
|
className="tooltip tooltip-right"
|
||||||
disabled={disabled}
|
data-tip="Use template variables like {servername}, {status}, etc."
|
||||||
/>
|
>
|
||||||
<span className="text-sm text-base-content/70">{unit}</span>
|
<CircleHelp className="w-4 h-4 text-base-content/50" />
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div className="flex items-center gap-2 flex-1">
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="input input-bordered input-sm flex-1"
|
className="input input-bordered input-sm w-full"
|
||||||
placeholder={`${label} alert message...`}
|
placeholder={`Enter ${label.toLowerCase()} notification message...`}
|
||||||
value={message}
|
value={message}
|
||||||
onChange={(e) => onMessageChange(e.target.value)}
|
onChange={(e) => onMessageChange(e.target.value)}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
<div className="tooltip" data-tip="Notification message template">
|
|
||||||
<CircleHelp className="w-4 h-4 text-base-content/50" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -132,54 +179,66 @@ export const NotificationSettings = ({ onError, onSuccess }: NotificationSetting
|
|||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="w-full bg-base-200 p-6 rounded-2xl border border-stone-800">
|
||||||
<div className="w-full bg-base-200 p-4 rounded-2xl border border-stone-800">
|
{/* Header */}
|
||||||
{/* Header */}
|
<div className="text-center lg:text-left mb-6">
|
||||||
<div className="text-center lg:text-left">
|
<h2 className="text-xl font-bold">Notification Settings</h2>
|
||||||
<h2 className="text-lg font-bold">Notification Settings</h2>
|
<p className="text-sm opacity-70 mt-1">Configure monitoring alerts for your servers and applications</p>
|
||||||
<p className="text-sm opacity-70">Configure monitoring alerts for your servers and applications</p>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Main Content */}
|
<div className="space-y-8">
|
||||||
<div className="grid gap-6 mt-4">
|
{/* Server Monitoring Section */}
|
||||||
{/* Server Monitoring Section */}
|
<div className="card bg-base-100 shadow-lg border border-base-300">
|
||||||
<div className="card bg-base-100 shadow-lg border border-base-300">
|
<div className={`card-body p-6 ${serverCollapsed ? "pb-2" : ""}`}>
|
||||||
<div className="card-body">
|
<div
|
||||||
<div className="flex items-center gap-3 mb-6">
|
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`}
|
||||||
<div className="p-2 bg-primary/10 rounded-lg">
|
onClick={() => setServerCollapsed(!serverCollapsed)}
|
||||||
<Server className="w-6 h-6 text-primary" />
|
>
|
||||||
</div>
|
<div className="p-2 bg-primary/10 rounded-lg">
|
||||||
<div>
|
<Server className="w-6 h-6 text-primary" />
|
||||||
<h3 className="text-lg font-bold">Server Monitoring</h3>
|
|
||||||
<p className="text-sm opacity-70">Monitor server resources and performance</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<h3 className="text-lg font-bold">Server Monitoring</h3>
|
||||||
|
<p className="text-sm opacity-70">Monitor server resources and performance</p>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
className={`px-3 py-1 rounded-full text-xs font-medium ${
|
||||||
|
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>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={`transition-all duration-300 overflow-hidden ${serverCollapsed ? "max-h-0 opacity-0" : "max-h-none opacity-100"}`}
|
||||||
|
>
|
||||||
{/* Master Toggle */}
|
{/* Master Toggle */}
|
||||||
<div className="card bg-base-200 mb-6">
|
<div className="card bg-base-200 mb-6">
|
||||||
<div className="card-body p-4">
|
<div className="card-body p-4">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center gap-3">
|
||||||
<div className="flex items-center gap-3">
|
<input
|
||||||
<input
|
type="checkbox"
|
||||||
type="checkbox"
|
className="toggle toggle-primary"
|
||||||
className="toggle toggle-primary toggle-lg"
|
checked={serverSettings?.enabled}
|
||||||
checked={serverSettings?.enabled}
|
onChange={() => setServerSettings({ ...serverSettings, enabled: !serverSettings.enabled })}
|
||||||
onChange={() => setServerSettings({ ...serverSettings, enabled: !serverSettings.enabled })}
|
/>
|
||||||
/>
|
<div>
|
||||||
<div>
|
<h4 className="font-semibold">Enable Server Monitoring</h4>
|
||||||
<h3 className="font-semibold">Enable Server Monitoring</h3>
|
<p className="text-sm text-base-content/70">Turn on global server monitoring notifications</p>
|
||||||
<p className="text-sm text-base-content/70">Turn on global server monitoring</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className={`badge ${serverSettings?.enabled ? "badge-success" : "badge-neutral"}`}>
|
|
||||||
{serverSettings?.enabled ? "Active" : "Inactive"}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Status Change Alerts */}
|
{/* Settings Content */}
|
||||||
<div className={`space-y-4 ${!serverSettings?.enabled ? "opacity-50" : ""}`}>
|
<div className={`space-y-6 ${!serverSettings?.enabled ? "opacity-50" : ""}`}>
|
||||||
|
{/* Status Change Alerts */}
|
||||||
<div className="card bg-base-200">
|
<div className="card bg-base-200">
|
||||||
<div className="card-body p-4">
|
<div className="card-body p-4">
|
||||||
<div className="flex flex-col lg:flex-row lg:items-center gap-4">
|
<div className="flex flex-col lg:flex-row lg:items-center gap-4">
|
||||||
@ -209,7 +268,7 @@ export const NotificationSettings = ({ onError, onSuccess }: NotificationSetting
|
|||||||
}
|
}
|
||||||
disabled={!serverSettings?.enabled}
|
disabled={!serverSettings?.enabled}
|
||||||
/>
|
/>
|
||||||
<div className="tooltip" data-tip="Notification message template">
|
<div className="tooltip tooltip-left" data-tip="Notification message template">
|
||||||
<CircleHelp className="w-4 h-4 text-base-content/50" />
|
<CircleHelp className="w-4 h-4 text-base-content/50" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -218,13 +277,9 @@ export const NotificationSettings = ({ onError, onSuccess }: NotificationSetting
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Resource Thresholds */}
|
{/* Resource Thresholds */}
|
||||||
<div className="space-y-4">
|
<div>
|
||||||
<div className="flex items-center gap-2 mb-4">
|
<h4 className="text-base font-semibold mb-4">Resource Thresholds</h4>
|
||||||
<h4 className="text-base font-semibold">Resource Thresholds</h4>
|
<div className="space-y-0">
|
||||||
<div className="badge badge-outline badge-sm">Configure limits</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-3">
|
|
||||||
<ResourceThresholdRow
|
<ResourceThresholdRow
|
||||||
label="CPU Usage"
|
label="CPU Usage"
|
||||||
checked={serverSettings?.notificationCpu}
|
checked={serverSettings?.notificationCpu}
|
||||||
@ -261,9 +316,11 @@ export const NotificationSettings = ({ onError, onSuccess }: NotificationSetting
|
|||||||
}
|
}
|
||||||
value={serverSettings?.memoryLimit}
|
value={serverSettings?.memoryLimit}
|
||||||
onValueChange={(value) => setServerSettings({ ...serverSettings, memoryLimit: value })}
|
onValueChange={(value) => setServerSettings({ ...serverSettings, memoryLimit: value })}
|
||||||
unit="GB"
|
unit="%"
|
||||||
message={serverSettings?.notificationTextMemory}
|
message={serverSettings?.notificationTextMemory}
|
||||||
onMessageChange={(value) => setServerSettings({ ...serverSettings, notificationTextMemory: value })}
|
onMessageChange={(value) =>
|
||||||
|
setServerSettings({ ...serverSettings, notificationTextMemory: value })
|
||||||
|
}
|
||||||
disabled={!serverSettings?.enabled}
|
disabled={!serverSettings?.enabled}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@ -301,50 +358,105 @@ export const NotificationSettings = ({ onError, onSuccess }: NotificationSetting
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Application Monitoring Section */}
|
{/* Application Monitoring Section */}
|
||||||
<div className="card bg-base-100 shadow-lg border border-base-300">
|
<div className="card bg-base-100 shadow-lg border border-base-300">
|
||||||
<div className="card-body">
|
<div className={`card-body p-6 ${applicationCollapsed ? "pb-2" : ""}`}>
|
||||||
<div className="flex items-center gap-3 mb-6">
|
<div
|
||||||
<div className="p-2 bg-secondary/10 rounded-lg">
|
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`}
|
||||||
<Smartphone className="w-6 h-6 text-secondary" />
|
onClick={() => setApplicationCollapsed(!applicationCollapsed)}
|
||||||
</div>
|
>
|
||||||
<div>
|
<div className="p-2 bg-secondary/10 rounded-lg">
|
||||||
<h3 className="text-lg font-bold">Application Monitoring</h3>
|
<Smartphone className="w-6 h-6 text-secondary" />
|
||||||
<p className="text-sm opacity-70">Monitor application performance and availability</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<h3 className="text-lg font-bold">Application Monitoring</h3>
|
||||||
|
<p className="text-sm opacity-70">Configure application monitoring notifications</p>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
className={`px-3 py-1 rounded-full text-xs font-medium ${
|
||||||
|
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>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={`transition-all duration-300 overflow-hidden ${applicationCollapsed ? "max-h-0 opacity-0" : "max-h-none opacity-100"}`}
|
||||||
|
>
|
||||||
{/* Master Toggle */}
|
{/* Master Toggle */}
|
||||||
<div className="card bg-base-200 mb-6">
|
<div className="card bg-base-200 mb-6">
|
||||||
<div className="card-body p-4">
|
<div className="card-body p-4">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center gap-3">
|
||||||
<div className="flex items-center gap-3">
|
<input
|
||||||
<input
|
type="checkbox"
|
||||||
type="checkbox"
|
className="toggle toggle-secondary"
|
||||||
className="toggle toggle-secondary toggle-lg"
|
checked={applicationsSettings?.enabled}
|
||||||
checked={applicationsSettings?.enabled}
|
onChange={() =>
|
||||||
onChange={() =>
|
setApplicationsSettings({ ...applicationsSettings, enabled: !applicationsSettings.enabled })
|
||||||
setApplicationsSettings({ ...applicationsSettings, enabled: !applicationsSettings.enabled })
|
}
|
||||||
}
|
/>
|
||||||
/>
|
<div>
|
||||||
<div>
|
<h4 className="font-semibold">Enable Application Monitoring</h4>
|
||||||
<h3 className="font-semibold">Enable Application Monitoring</h3>
|
<p className="text-sm text-base-content/70">Turn on global application monitoring</p>
|
||||||
<p className="text-sm text-base-content/70">Turn on global application monitoring</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className={`badge ${applicationsSettings?.enabled ? "badge-success" : "badge-neutral"}`}>
|
|
||||||
{applicationsSettings?.enabled ? "Active" : "Inactive"}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Application Settings */}
|
{/* Application Settings */}
|
||||||
<div className={`space-y-4 ${!applicationsSettings?.enabled ? "opacity-50" : ""}`}>
|
<div className={`space-y-6 ${!applicationsSettings?.enabled ? "opacity-50" : ""}`}>
|
||||||
{/* Status Change Alerts */}
|
{/* Status Change Alerts */}
|
||||||
<div className="card bg-base-200">
|
<div className="card bg-base-200">
|
||||||
<div className="card-body p-4">
|
<div className="card-body p-4">
|
||||||
@ -378,7 +490,7 @@ export const NotificationSettings = ({ onError, onSuccess }: NotificationSetting
|
|||||||
}
|
}
|
||||||
disabled={!applicationsSettings?.enabled}
|
disabled={!applicationsSettings?.enabled}
|
||||||
/>
|
/>
|
||||||
<div className="tooltip" data-tip="Notification message template">
|
<div className="tooltip tooltip-left" data-tip="Notification message template">
|
||||||
<CircleHelp className="w-4 h-4 text-base-content/50" />
|
<CircleHelp className="w-4 h-4 text-base-content/50" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -386,13 +498,9 @@ export const NotificationSettings = ({ onError, onSuccess }: NotificationSetting
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Latency Threshold */}
|
{/* Performance Thresholds */}
|
||||||
<div className="space-y-4">
|
<div>
|
||||||
<div className="flex items-center gap-2 mb-4">
|
<h4 className="text-base font-semibold mb-4">Performance Thresholds</h4>
|
||||||
<h4 className="text-base font-semibold">Performance Thresholds</h4>
|
|
||||||
<div className="badge badge-outline badge-sm">Configure limits</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ResourceThresholdRow
|
<ResourceThresholdRow
|
||||||
label="Latency Threshold"
|
label="Latency Threshold"
|
||||||
checked={applicationsSettings?.notificationLatency}
|
checked={applicationsSettings?.notificationLatency}
|
||||||
@ -403,7 +511,9 @@ export const NotificationSettings = ({ onError, onSuccess }: NotificationSetting
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
value={applicationsSettings?.latencyLimit}
|
value={applicationsSettings?.latencyLimit}
|
||||||
onValueChange={(value) => setApplicationsSettings({ ...applicationsSettings, latencyLimit: Number(value) })}
|
onValueChange={(value) =>
|
||||||
|
setApplicationsSettings({ ...applicationsSettings, latencyLimit: Number(value) })
|
||||||
|
}
|
||||||
unit="ms"
|
unit="ms"
|
||||||
message={applicationsSettings?.notificationTextLatency}
|
message={applicationsSettings?.notificationTextLatency}
|
||||||
onMessageChange={(value) =>
|
onMessageChange={(value) =>
|
||||||
@ -412,14 +522,53 @@ export const NotificationSettings = ({ onError, onSuccess }: NotificationSetting
|
|||||||
disabled={!applicationsSettings?.enabled}
|
disabled={!applicationsSettings?.enabled}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Action Buttons */}
|
{/* Action Buttons */}
|
||||||
<div className="flex flex-col sm:flex-row gap-3 justify-end">
|
<div className="flex justify-end">
|
||||||
<button className="btn btn-primary" onClick={saveNotificationSettings}>Save Settings</button>
|
<button className="btn btn-primary" onClick={saveNotificationSettings}>
|
||||||
</div>
|
Save Settings
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -97,7 +97,7 @@ const useNotifications = () => {
|
|||||||
const editNotificationApplicationsSettings = (settings: any): Promise<string> | string => {
|
const editNotificationApplicationsSettings = (settings: any): Promise<string> | string => {
|
||||||
return axios.post('/api/notifications/settings_applications_edit', settings)
|
return axios.post('/api/notifications/settings_applications_edit', settings)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
return response.data.notificationSettings;
|
return response.data.notification;
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
throw err.response?.data?.error || 'An error occurred';
|
throw err.response?.data?.error || 'An error occurred';
|
||||||
@ -107,7 +107,7 @@ const useNotifications = () => {
|
|||||||
const editNotificationServerSettings = (settings: any): Promise<string> | string => {
|
const editNotificationServerSettings = (settings: any): Promise<string> | string => {
|
||||||
return axios.post('/api/notifications/settings_server_edit', settings)
|
return axios.post('/api/notifications/settings_server_edit', settings)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
return response.data.notificationSettings;
|
return response.data.notification;
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
throw err.response?.data?.error || 'An error occurred';
|
throw err.response?.data?.error || 'An error occurred';
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user