Files
openhuman/src/components/daemon/DaemonHealthIndicator.tsx
T
cyrus a6eeb00e88 Add daemon lifecycle management system to frontend UI
This implementation integrates the Rust daemon's health monitoring and lifecycle management into the React frontend. Key features include real-time health indicators, automatic start/error recovery, and detailed control panels, enhancing both user experience and operational reliability.
2026-02-23 16:05:27 +05:30

125 lines
2.9 KiB
TypeScript

/**
* Daemon Health Indicator
*
* Compact status indicator showing daemon health with a colored dot and optional label.
* Can be clicked to show detailed health information.
*/
import type React from 'react';
import { useDaemonHealth, formatRelativeTime } from '../../hooks/useDaemonHealth';
import type { DaemonStatus } from '../../store/daemonSlice';
interface Props {
userId?: string;
size?: 'sm' | 'md' | 'lg';
showLabel?: boolean;
onClick?: () => void;
className?: string;
}
const DaemonHealthIndicator: React.FC<Props> = ({
userId,
size = 'md',
showLabel = false,
onClick,
className = '',
}) => {
const daemonHealth = useDaemonHealth(userId);
// Size configurations
const sizeConfig = {
sm: {
dot: 'w-2 h-2',
text: 'text-xs',
container: 'gap-1.5',
},
md: {
dot: 'w-3 h-3',
text: 'text-sm',
container: 'gap-2',
},
lg: {
dot: 'w-4 h-4',
text: 'text-base',
container: 'gap-2.5',
},
};
const config = sizeConfig[size];
// Status color mapping
const getStatusColor = (status: DaemonStatus): string => {
switch (status) {
case 'running':
return 'bg-green-500';
case 'starting':
return 'bg-yellow-500';
case 'error':
return 'bg-red-500';
case 'disconnected':
default:
return 'bg-gray-500';
}
};
// Status text mapping
const getStatusText = (status: DaemonStatus): string => {
switch (status) {
case 'running':
return 'Running';
case 'starting':
return 'Starting';
case 'error':
return 'Error';
case 'disconnected':
default:
return 'Disconnected';
}
};
// Tooltip content
const getTooltipContent = (): string => {
const { status, componentCount, healthyComponentCount, errorComponentCount, lastUpdate } = daemonHealth;
let tooltip = `Status: ${getStatusText(status)}`;
if (componentCount > 0) {
tooltip += `\nComponents: ${healthyComponentCount}/${componentCount} healthy`;
if (errorComponentCount > 0) {
tooltip += ` (${errorComponentCount} errors)`;
}
}
if (lastUpdate) {
tooltip += `\nLast update: ${formatRelativeTime(lastUpdate)}`;
}
return tooltip;
};
const statusColor = getStatusColor(daemonHealth.status);
const statusText = getStatusText(daemonHealth.status);
const containerClasses = `
flex items-center ${config.container}
${onClick ? 'cursor-pointer hover:opacity-80 transition-opacity' : ''}
${className}
`.trim();
return (
<div
className={containerClasses}
onClick={onClick}
title={getTooltipContent()}
>
<div className={`${config.dot} rounded-full ${statusColor} flex-shrink-0`} />
{showLabel && (
<span className={`${config.text} text-gray-300 font-medium`}>
{statusText}
</span>
)}
</div>
);
};
export default DaemonHealthIndicator;