Update notification service

This commit is contained in:
MacRimi
2026-03-10 18:00:56 +01:00
parent e4aa081e64
commit 8c5ccbadac
2 changed files with 85 additions and 26 deletions

View File

@@ -131,7 +131,6 @@ export function HealthStatusModal({ open, onOpenChange, getApiUrl }: HealthStatu
const response = await fetch(getApiUrl("/api/health/full"), { headers: authHeaders }) const response = await fetch(getApiUrl("/api/health/full"), { headers: authHeaders })
let infoCount = 0 let infoCount = 0
let dismissedCount = 0
if (!response.ok) { if (!response.ok) {
// Fallback to legacy endpoint // Fallback to legacy endpoint
@@ -158,21 +157,34 @@ export function HealthStatusModal({ open, onOpenChange, getApiUrl }: HealthStatu
setDismissedItems(fullData.dismissed || []) setDismissedItems(fullData.dismissed || [])
setCustomSuppressions(fullData.custom_suppressions || []) setCustomSuppressions(fullData.custom_suppressions || [])
newOverallStatus = fullData.health?.overall || "OK" newOverallStatus = fullData.health?.overall || "OK"
dismissedCount = (fullData.dismissed || []).length
// Count INFO categories // Get categories that have dismissed items (these become INFO)
const customCats = new Set((fullData.custom_suppressions || []).map((cs: { category: string }) => cs.category))
const filteredDismissed = (fullData.dismissed || []).filter((item: { category: string }) => !customCats.has(item.category))
const categoriesWithDismissed = new Set<string>()
filteredDismissed.forEach((item: { category: string }) => {
const catMeta = CATEGORIES.find(c => c.category === item.category || c.key === item.category)
if (catMeta) {
categoriesWithDismissed.add(catMeta.key)
}
})
// Count effective INFO categories (original INFO + OK categories with dismissed)
if (fullData.health?.details) { if (fullData.health?.details) {
CATEGORIES.forEach(({ key }) => { CATEGORIES.forEach(({ key }) => {
const cat = fullData.health.details[key as keyof typeof fullData.health.details] const cat = fullData.health.details[key as keyof typeof fullData.health.details]
if (cat && cat.status?.toUpperCase() === "INFO") { if (cat) {
infoCount++ const originalStatus = cat.status?.toUpperCase()
// Count as INFO if: originally INFO OR (originally OK and has dismissed items)
if (originalStatus === "INFO" || (originalStatus === "OK" && categoriesWithDismissed.has(key))) {
infoCount++
}
} }
}) })
} }
} }
// Total info = INFO categories + dismissed items const totalInfoCount = infoCount
const totalInfoCount = infoCount + dismissedCount
// Emit event with the FRESH data from the response, not the stale state // Emit event with the FRESH data from the response, not the stale state
const event = new CustomEvent("healthStatusUpdated", { const event = new CustomEvent("healthStatusUpdated", {
@@ -271,10 +283,55 @@ export function HealthStatusModal({ open, onOpenChange, getApiUrl }: HealthStatu
} }
} }
const getHealthStats = () => { // Get categories that have dismissed items (to show as INFO)
if (!healthData?.details) { const getCategoriesWithDismissed = () => {
return { total: 0, healthy: 0, info: 0, warnings: 0, critical: 0, unknown: 0 } const customCats = new Set(customSuppressions.map(cs => cs.category))
const filteredDismissed = dismissedItems.filter(item => !customCats.has(item.category))
const categoriesWithDismissed = new Set<string>()
filteredDismissed.forEach(item => {
// Map dismissed category to our CATEGORIES keys
const catMeta = CATEGORIES.find(c => c.category === item.category || c.key === item.category)
if (catMeta) {
categoriesWithDismissed.add(catMeta.key)
}
})
return categoriesWithDismissed
}
const categoriesWithDismissed = getCategoriesWithDismissed()
// Get effective status for a category (considers dismissed items)
const getEffectiveStatus = (key: string, originalStatus: string) => {
// If category has dismissed items and original status is OK, show as INFO
if (categoriesWithDismissed.has(key) && originalStatus?.toUpperCase() === "OK") {
return "INFO"
} }
return originalStatus?.toUpperCase() || "UNKNOWN"
}
const getHealthStats = () => {
if (!healthData?.details) return { total: 0, healthy: 0, info: 0, warnings: 0, critical: 0, unknown: 0 }
let healthy = 0
let info = 0
let warnings = 0
let critical = 0
let unknown = 0
CATEGORIES.forEach(({ key }) => {
const categoryData = healthData.details[key as keyof typeof healthData.details]
if (categoryData) {
const effectiveStatus = getEffectiveStatus(key, categoryData.status)
if (effectiveStatus === "OK") healthy++
else if (effectiveStatus === "INFO") info++
else if (effectiveStatus === "WARNING") warnings++
else if (effectiveStatus === "CRITICAL") critical++
else if (effectiveStatus === "UNKNOWN") unknown++
}
})
return { total: CATEGORIES.length, healthy, info, warnings, critical, unknown }
}
let healthy = 0 let healthy = 0
let info = 0 let info = 0
@@ -575,7 +632,8 @@ export function HealthStatusModal({ open, onOpenChange, getApiUrl }: HealthStatu
<div className="space-y-2"> <div className="space-y-2">
{CATEGORIES.map(({ key, label, Icon }) => { {CATEGORIES.map(({ key, label, Icon }) => {
const categoryData = healthData.details[key as keyof typeof healthData.details] const categoryData = healthData.details[key as keyof typeof healthData.details]
const status = categoryData?.status || "UNKNOWN" const originalStatus = categoryData?.status || "UNKNOWN"
const status = getEffectiveStatus(key, originalStatus)
const reason = categoryData?.reason const reason = categoryData?.reason
const checks = categoryData?.checks const checks = categoryData?.checks
const isExpanded = expandedCategories.has(key) const isExpanded = expandedCategories.has(key)

View File

@@ -357,17 +357,18 @@ export function ProxmoxDashboard() {
</div> </div>
</div> </div>
{systemStatus.status === "healthy" && infoCount > 0 ? ( <div className="flex flex-col items-end gap-1">
<Badge variant="outline" className="bg-blue-500/10 text-blue-500 border-blue-500/20">
<Info className="h-4 w-4" />
<span className="ml-1">{infoCount} info</span>
</Badge>
) : (
<Badge variant="outline" className={statusColor}> <Badge variant="outline" className={statusColor}>
{statusIcon} {statusIcon}
<span className="ml-1 capitalize">{systemStatus.status}</span> <span className="ml-1 capitalize">{systemStatus.status}</span>
</Badge> </Badge>
)} {systemStatus.status === "healthy" && infoCount > 0 && (
<Badge variant="outline" className="bg-blue-500/10 text-blue-500 border-blue-500/20">
<Info className="h-4 w-4" />
<span className="ml-1">{infoCount} info</span>
</Badge>
)}
</div>
<div className="text-sm text-muted-foreground whitespace-nowrap"> <div className="text-sm text-muted-foreground whitespace-nowrap">
Uptime: {systemStatus.uptime || "N/A"} Uptime: {systemStatus.uptime || "N/A"}
@@ -394,17 +395,17 @@ export function ProxmoxDashboard() {
{/* Mobile Actions */} {/* Mobile Actions */}
<div className="flex lg:hidden items-center gap-2"> <div className="flex lg:hidden items-center gap-2">
{systemStatus.status === "healthy" && infoCount > 0 ? ( <div className="flex flex-col items-center gap-1">
<Badge variant="outline" className="bg-blue-500/10 text-blue-500 border-blue-500/20 text-xs px-2">
<Info className="h-4 w-4" />
<span className="ml-1">{infoCount}</span>
</Badge>
) : (
<Badge variant="outline" className={`${statusColor} text-xs px-2`}> <Badge variant="outline" className={`${statusColor} text-xs px-2`}>
{statusIcon} {statusIcon}
<span className="ml-1 capitalize hidden sm:inline">{systemStatus.status}</span>
</Badge> </Badge>
)} {systemStatus.status === "healthy" && infoCount > 0 && (
<Badge variant="outline" className="bg-blue-500/10 text-blue-500 border-blue-500/20 text-xs px-2">
<Info className="h-4 w-4" />
<span className="ml-1">{infoCount}</span>
</Badge>
)}
</div>
<Button <Button
variant="ghost" variant="ghost"