fix: reduce false container-failed notifications, add error details to UI
Backend (health_monitor.py): - Skip health checks for instances with no container_id - Treat 'not_found' as error only when container was previously running - Skip duplicate error notifications when already in error state - Skip 'not_found' notifications for containers that never ran Frontend (notification-item.tsx): - Display notification.message (detailed error text) - Add expandable Details section showing metadata (exit_code, previous_status, etc.) - New CSS styles for message and metadata display Quality gates: py_compile, tsc --noEmit, 80/80 tests pass
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { useState } from "react";
|
||||
import { Icon } from "../../icon";
|
||||
import { formatRelativeTime } from "../../../utils/time";
|
||||
import type { NotificationItem as NotificationItemType } from "../../../api/notifications";
|
||||
@@ -17,6 +18,18 @@ const severityIconMap: Record<string, IconName> = {
|
||||
success: "success",
|
||||
};
|
||||
|
||||
function formatMetadataValue(value: unknown): string {
|
||||
if (value === null || value === undefined) return "—";
|
||||
if (typeof value === "string") return value;
|
||||
if (typeof value === "number") return String(value);
|
||||
if (typeof value === "boolean") return value ? "Yes" : "No";
|
||||
try {
|
||||
return JSON.stringify(value);
|
||||
} catch {
|
||||
return String(value);
|
||||
}
|
||||
}
|
||||
|
||||
export function NotificationItem({
|
||||
notification,
|
||||
onMarkRead,
|
||||
@@ -24,6 +37,11 @@ export function NotificationItem({
|
||||
}: NotificationItemProps) {
|
||||
const isUnread = notification.read_at === null;
|
||||
const iconName = severityIconMap[notification.severity] ?? "info";
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
const hasDetails =
|
||||
!!notification.message ||
|
||||
Object.keys(notification.metadata || {}).length > 0;
|
||||
|
||||
return (
|
||||
<li
|
||||
@@ -35,11 +53,36 @@ export function NotificationItem({
|
||||
</div>
|
||||
<div className="notification-item-content">
|
||||
<div className="notification-item-title">{notification.title}</div>
|
||||
{notification.message && (
|
||||
<div className="notification-item-message">
|
||||
{notification.message}
|
||||
</div>
|
||||
)}
|
||||
<div className="notification-item-time">
|
||||
{formatRelativeTime(notification.created_at)}
|
||||
</div>
|
||||
{expanded && notification.metadata && (
|
||||
<dl className="notification-item-metadata">
|
||||
{Object.entries(notification.metadata).map(([key, value]) => (
|
||||
<div key={key} className="notification-metadata-row">
|
||||
<dt>{key.replace(/_/g, " ")}</dt>
|
||||
<dd>{formatMetadataValue(value)}</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
)}
|
||||
</div>
|
||||
<div className="notification-item-actions">
|
||||
{hasDetails && (
|
||||
<button
|
||||
type="button"
|
||||
className="notification-item-action"
|
||||
onClick={() => setExpanded(!expanded)}
|
||||
aria-label={expanded ? "Hide details" : "Show details"}
|
||||
>
|
||||
{expanded ? "Less" : "Details"}
|
||||
</button>
|
||||
)}
|
||||
{isUnread && (
|
||||
<button
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user