Pulse/frontend-modern/src/components/shared/NodeGroupHeader.tsx
courtmanr@gmail.com ebcafa1dac feat: adaptive node table layout, guest row fixes, and legacy agent detection
- Implemented adaptive layout for NodeSummaryTable with responsive columns and sticky name column.
- Fixed GuestRow background display issues.
- Added IsLegacy field to Host and DockerHost models to flag legacy agents (version < 1.0.0).
- Updated monitor to populate IsLegacy based on agent version.
2025-11-25 17:19:36 +00:00

78 lines
2.6 KiB
TypeScript

import { Component, Show } from 'solid-js';
import type { Node } from '@/types/api';
import { getNodeDisplayName, hasAlternateDisplayName } from '@/utils/nodes';
import { StatusDot } from '@/components/shared/StatusDot';
import { getNodeStatusIndicator } from '@/utils/status';
interface NodeGroupHeaderProps {
node: Node;
colspan?: number;
renderAs?: 'tr' | 'div';
}
export const NodeGroupHeader: Component<NodeGroupHeaderProps> = (props) => {
const nodeStatus = () => getNodeStatusIndicator(props.node);
const isOnline = () => nodeStatus().variant === 'success';
const nodeUrl = () => props.node.guestURL || props.node.host || `https://${props.node.name}:8006`;
const displayName = () => getNodeDisplayName(props.node);
const showActualName = () => hasAlternateDisplayName(props.node);
const InnerContent = () => (
<div
class={`flex flex-wrap items-center gap-3 ${isOnline() ? '' : 'opacity-60'}`}
title={nodeStatus().label}
>
<StatusDot
variant={nodeStatus().variant}
title={nodeStatus().label}
ariaLabel={nodeStatus().label}
size="xs"
/>
<a
href={nodeUrl()}
target="_blank"
rel="noopener noreferrer"
class="transition-colors duration-150 hover:text-sky-600 dark:hover:text-sky-400"
title={`Open ${props.node.name} web interface`}
>
{displayName()}
</a>
<Show when={showActualName()}>
<span class="text-[10px] text-slate-500 dark:text-slate-400">({props.node.name})</span>
</Show>
<Show when={props.node.isClusterMember !== undefined}>
<span
class={`rounded px-2 py-0.5 text-[10px] font-medium whitespace-nowrap ${props.node.isClusterMember
? 'bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-300'
: 'bg-slate-200 text-slate-600 dark:bg-slate-700/60 dark:text-slate-300'
}`}
>
{props.node.isClusterMember ? props.node.clusterName : 'Standalone'}
</span>
</Show>
</div>
);
return (
<Show
when={props.renderAs === 'tr'}
fallback={
<div class="bg-gray-50 dark:bg-gray-900/40 w-full">
<div class="py-1 pr-2 pl-4 text-[12px] sm:text-sm font-semibold text-slate-700 dark:text-slate-100">
<InnerContent />
</div>
</div>
}
>
<tr class="bg-gray-50 dark:bg-gray-900/40">
<td
colspan={props.colspan}
class="py-1 pr-2 pl-4 text-[12px] sm:text-sm font-semibold text-slate-700 dark:text-slate-100"
>
<InnerContent />
</td>
</tr>
</Show>
);
};