mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-05-01 03:46:22 +00:00
Update notification service
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user