+ {/* Category row -- entire block is clickable to expand/collapse */}
+
{
+ if (!isEnabled) return
+ setExpandedCategories(prev => {
+ const next = new Set(prev)
+ const key = `${chName}.${cat.key}`
+ if (next.has(key)) next.delete(key)
+ else next.add(key)
+ return next
+ })
+ }}
+ >
+ {/* Expand arrow */}
+
+ {/* Label */}
- {cat.label}
- {cat.desc}
+ {/* Count badge */}
{isEnabled && eventsForGroup.length > 0 && (
-
+
{enabledCount}/{eventsForGroup.length}
)}
+ {/* Toggle -- same style as channel enable toggle */}
{
+ } ${isEnabled ? "bg-blue-600" : "bg-muted-foreground/30"}`}
+ onClick={(e) => {
+ e.stopPropagation()
if (!editMode) return
updateConfig(p => {
const ch = { ...(p.channel_overrides?.[chName] || { categories: {}, events: {} }) }
const newEnabled = !isEnabled
const newEvents = { ...(ch.events || {}) }
- // When enabling, turn all sub-events on
if (newEnabled && eventsForGroup.length > 0) {
for (const evt of eventsForGroup) {
newEvents[evt.type] = true
@@ -311,19 +311,20 @@ export function NotificationSettings() {
})
}}
>
-
+ {/* Sub-event toggles */}
{isEnabled && isExpanded && eventsForGroup.length > 0 && (
-
+
{eventsForGroup.map(evt => {
const evtEnabled = overrides.events?.[evt.type] ?? evt.default_enabled
return (
-
-
+
+
{evt.title}
{
if (!editMode) return
updateConfig(p => {
@@ -348,8 +349,8 @@ export function NotificationSettings() {
})
}}
>
-
@@ -876,7 +877,7 @@ matcher: proxmenux-pbs
onChange={e => updateChannel("telegram", "chat_id", e.target.value)}
/>
- {renderChannelCategories("telegram", "blue")}
+ {renderChannelCategories("telegram")}
{/* Per-channel action bar */}
- {renderChannelCategories("gotify", "green")}
+ {renderChannelCategories("gotify")}
{/* Per-channel action bar */}
- {renderChannelCategories("discord", "indigo")}
+ {renderChannelCategories("discord")}
{/* Per-channel action bar */}
- {renderChannelCategories("email", "amber")}
+ {renderChannelCategories("email")}
{/* Per-channel action bar */}
{/* close bordered channel container */}
- {/* ── Proxmox Webhook ── */}
-
-
-
- Proxmox Webhook
-
-
-
-
- PVE Webhook Configuration
-
- {!editMode && (
-
{
- try {
- setWebhookSetup({ status: "running", fallback_commands: [], error: "" })
- const setup = await fetchApi<{
- configured: boolean; secret?: string; fallback_commands?: string[]; error?: string
- }>("/api/notifications/proxmox/setup-webhook", { method: "POST" })
- if (setup.configured) {
- setWebhookSetup({ status: "success", fallback_commands: [], error: "" })
- if (setup.secret) {
- const updated = { ...config, webhook_secret: setup.secret }
- setConfig(updated)
- setOriginalConfig(updated)
- }
- } else {
- setWebhookSetup({ status: "failed", fallback_commands: setup.fallback_commands || [], error: setup.error || "" })
- }
- } catch {
- setWebhookSetup({ status: "failed", fallback_commands: [], error: "Request failed" })
- }
- }}
- disabled={webhookSetup.status === "running"}
- >
- {webhookSetup.status === "running" ? : }
- Re-configure PVE
-
- )}
-
-
- {/* Setup status inline */}
- {webhookSetup.status === "success" && (
-
-
-
PVE webhook configured successfully.
-
- )}
- {webhookSetup.status === "failed" && (
-
-
-
-
PVE auto-config failed: {webhookSetup.error}
-
- {webhookSetup.fallback_commands.length > 0 && (
-
-{webhookSetup.fallback_commands.join('\n')}
-
- )}
-
- )}
-
-
-
Shared Secret
-
- updateConfig(p => ({ ...p, webhook_secret: e.target.value }))}
- disabled={!editMode}
- />
- toggleSecret("wh_secret")}
- >
- {showSecrets["wh_secret"] ? : }
-
-
-
- {"Used for remote connections only (e.g. PBS on another host). Local PVE webhook runs on localhost and does not need this header."}
-
-
-
-
Allowed IPs (optional, remote only)
-
updateConfig(p => ({ ...p, webhook_allowed_ips: e.target.value }))}
- disabled={!editMode}
- />
-
- {"Localhost (127.0.0.1) is always allowed. This restricts remote callers only."}
-
-
-
{/* close bordered webhook container */}
-
- {/* PBS manual guide (collapsible) */}
-
-
-
- Configure PBS notifications (manual)
-
-
-
- Backups launched from PVE are covered by the PVE webhook. PBS internal jobs
- (Verify, Prune, GC, Sync) require separate configuration on the PBS server.
-
-
- Append to /etc/proxmox-backup/notifications.cfg:
-
-
-{`webhook: proxmenux-webhook
-\tmethod post
-\turl http://:8008/api/notifications/webhook
-
-matcher: proxmenux-pbs
-\ttarget proxmenux-webhook
-\tmatch-severity warning,error`}
-
-
- {"Replace with this node's IP. Append at the end -- do not delete existing content."}
-
-
-
-
-
{/* ── Advanced: AI Enhancement ── */}
- {/* ── Notification History ── */}
-
-
setShowHistory(!showHistory)}
- >
- {showHistory ? : }
- Recent History
- {history.length > 0 && (
- {history.length}
- )}
-
- {showHistory && (
-
- {history.length === 0 ? (
-
No notifications sent yet
- ) : (
- <>
-
-
-
- Clear
-
-
-
- {history.map(entry => (
-
- {entry.success ? (
-
- ) : (
-
- )}
-
- {entry.title || entry.event_type}
-
- {entry.channel} - {new Date(entry.sent_at).toLocaleString()}
-
-
-
- {entry.severity}
-
-
- ))}
-
- >
- )}
-
- )}
-
>
)}
@@ -1492,7 +1299,7 @@ matcher: proxmenux-pbs
{config.enabled
- ? "Notifications are active. Events matching your severity filter and category selection will be sent to configured channels."
+ ? "Notifications are active. Each channel sends events based on its own category and event selection."
: "Enable notifications to receive alerts about system events, health status changes, and security incidents via Telegram, Gotify, Discord, or Email."}