mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-28 00:14:21 +00:00
feat(web): add tooltips for truncated fields in system status dialog
Wrap session_id, image, and mount path fields with Tooltip components so hovering over truncated text shows the full value.
This commit is contained in:
@@ -28,6 +28,12 @@ import {
|
|||||||
ApiRespBoxStatus,
|
ApiRespBoxStatus,
|
||||||
BoxSessionInfo,
|
BoxSessionInfo,
|
||||||
} from '@/app/infra/entities/api';
|
} from '@/app/infra/entities/api';
|
||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipProvider,
|
||||||
|
TooltipTrigger,
|
||||||
|
} from '@/components/ui/tooltip';
|
||||||
import { httpClient } from '@/app/infra/http/HttpClient';
|
import { httpClient } from '@/app/infra/http/HttpClient';
|
||||||
|
|
||||||
function StatusDot({ ok }: { ok: boolean | null }) {
|
function StatusDot({ ok }: { ok: boolean | null }) {
|
||||||
@@ -141,195 +147,216 @@ export default function SystemStatusCard({
|
|||||||
<DialogTitle>{t('monitoring.systemStatus')}</DialogTitle>
|
<DialogTitle>{t('monitoring.systemStatus')}</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<div className="space-y-5 overflow-y-auto flex-1 pr-1">
|
<TooltipProvider>
|
||||||
{/* Plugin Runtime */}
|
<div className="space-y-5 overflow-y-auto flex-1 pr-1">
|
||||||
<div className="space-y-2">
|
{/* Plugin Runtime */}
|
||||||
<div className="flex items-center gap-2">
|
<div className="space-y-2">
|
||||||
<Plug className="w-4 h-4 text-muted-foreground" />
|
<div className="flex items-center gap-2">
|
||||||
<span className="text-sm font-semibold">
|
<Plug className="w-4 h-4 text-muted-foreground" />
|
||||||
{t('monitoring.pluginRuntime')}
|
<span className="text-sm font-semibold">
|
||||||
</span>
|
{t('monitoring.pluginRuntime')}
|
||||||
</div>
|
|
||||||
<div className="ml-6 text-sm space-y-1">
|
|
||||||
<div className="flex items-center gap-1.5">
|
|
||||||
{pluginOk ? (
|
|
||||||
<CircleCheck className="w-4 h-4 text-green-600" />
|
|
||||||
) : (
|
|
||||||
<CircleX className="w-4 h-4 text-red-500" />
|
|
||||||
)}
|
|
||||||
<span
|
|
||||||
className={
|
|
||||||
pluginOk
|
|
||||||
? 'text-green-600 font-medium'
|
|
||||||
: 'text-red-500 font-medium'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{pluginOk
|
|
||||||
? t('monitoring.connected')
|
|
||||||
: pluginStatus && !pluginStatus.is_enable
|
|
||||||
? t('monitoring.disabled')
|
|
||||||
: t('monitoring.disconnected')}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{pluginStatus && !pluginStatus.is_enable && (
|
<div className="ml-6 text-sm space-y-1">
|
||||||
<p className="text-muted-foreground text-xs">
|
<div className="flex items-center gap-1.5">
|
||||||
{t('monitoring.pluginDisabled')}
|
{pluginOk ? (
|
||||||
</p>
|
<CircleCheck className="w-4 h-4 text-green-600" />
|
||||||
)}
|
) : (
|
||||||
{pluginStatus &&
|
<CircleX className="w-4 h-4 text-red-500" />
|
||||||
!pluginOk &&
|
|
||||||
pluginStatus.is_enable &&
|
|
||||||
pluginStatus.plugin_connector_error &&
|
|
||||||
pluginStatus.plugin_connector_error !== 'ok' && (
|
|
||||||
<p className="text-red-400 text-xs break-all">
|
|
||||||
{pluginStatus.plugin_connector_error}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="border-t" />
|
|
||||||
|
|
||||||
{/* Box Runtime */}
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Box className="w-4 h-4 text-muted-foreground" />
|
|
||||||
<span className="text-sm font-semibold">
|
|
||||||
{t('monitoring.boxRuntime')}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="ml-6 text-sm space-y-1">
|
|
||||||
<div className="flex items-center gap-1.5">
|
|
||||||
{boxOk ? (
|
|
||||||
<CircleCheck className="w-4 h-4 text-green-600" />
|
|
||||||
) : (
|
|
||||||
<CircleX className="w-4 h-4 text-red-500" />
|
|
||||||
)}
|
|
||||||
<span
|
|
||||||
className={
|
|
||||||
boxOk
|
|
||||||
? 'text-green-600 font-medium'
|
|
||||||
: 'text-red-500 font-medium'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{boxOk
|
|
||||||
? t('monitoring.connected')
|
|
||||||
: t('monitoring.disconnected')}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{boxStatus && !boxOk && boxStatus.connector_error && (
|
|
||||||
<p className="text-red-400 text-xs break-all">
|
|
||||||
{boxStatus.connector_error}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
{boxStatus && (
|
|
||||||
<div className="text-muted-foreground text-xs space-y-0.5">
|
|
||||||
{boxStatus.backend && (
|
|
||||||
<p>
|
|
||||||
{t('monitoring.boxBackend')}:{' '}
|
|
||||||
<span className="text-foreground font-mono">
|
|
||||||
{boxStatus.backend.name}
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
<p>
|
|
||||||
{t('monitoring.boxProfile')}:{' '}
|
|
||||||
<span className="text-foreground font-mono">
|
|
||||||
{boxStatus.profile}
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
{boxOk && boxStatus.active_sessions !== undefined && (
|
|
||||||
<p>
|
|
||||||
{t('monitoring.boxSandboxes')}:{' '}
|
|
||||||
<span className="text-foreground font-mono">
|
|
||||||
{boxStatus.active_sessions}
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
|
<span
|
||||||
|
className={
|
||||||
|
pluginOk
|
||||||
|
? 'text-green-600 font-medium'
|
||||||
|
: 'text-red-500 font-medium'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{pluginOk
|
||||||
|
? t('monitoring.connected')
|
||||||
|
: pluginStatus && !pluginStatus.is_enable
|
||||||
|
? t('monitoring.disabled')
|
||||||
|
: t('monitoring.disconnected')}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
{pluginStatus && !pluginStatus.is_enable && (
|
||||||
|
<p className="text-muted-foreground text-xs">
|
||||||
|
{t('monitoring.pluginDisabled')}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{pluginStatus &&
|
||||||
|
!pluginOk &&
|
||||||
|
pluginStatus.is_enable &&
|
||||||
|
pluginStatus.plugin_connector_error &&
|
||||||
|
pluginStatus.plugin_connector_error !== 'ok' && (
|
||||||
|
<p className="text-red-400 text-xs break-all">
|
||||||
|
{pluginStatus.plugin_connector_error}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Active Sandboxes */}
|
<div className="border-t" />
|
||||||
{boxSessions.length > 0 && (
|
|
||||||
<div className="mt-3 space-y-2">
|
{/* Box Runtime */}
|
||||||
{boxSessions.map((session) => (
|
<div className="space-y-2">
|
||||||
<div
|
<div className="flex items-center gap-2">
|
||||||
key={session.session_id}
|
<Box className="w-4 h-4 text-muted-foreground" />
|
||||||
className="rounded-lg border p-3 space-y-2"
|
<span className="text-sm font-semibold">
|
||||||
>
|
{t('monitoring.boxRuntime')}
|
||||||
<div className="flex items-center gap-1.5">
|
</span>
|
||||||
<Container className="w-4 h-4 text-muted-foreground flex-shrink-0" />
|
</div>
|
||||||
<span className="font-mono font-semibold text-foreground truncate text-sm">
|
<div className="ml-6 text-sm space-y-1">
|
||||||
{session.session_id}
|
<div className="flex items-center gap-1.5">
|
||||||
|
{boxOk ? (
|
||||||
|
<CircleCheck className="w-4 h-4 text-green-600" />
|
||||||
|
) : (
|
||||||
|
<CircleX className="w-4 h-4 text-red-500" />
|
||||||
|
)}
|
||||||
|
<span
|
||||||
|
className={
|
||||||
|
boxOk
|
||||||
|
? 'text-green-600 font-medium'
|
||||||
|
: 'text-red-500 font-medium'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{boxOk
|
||||||
|
? t('monitoring.connected')
|
||||||
|
: t('monitoring.disconnected')}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{boxStatus && !boxOk && boxStatus.connector_error && (
|
||||||
|
<p className="text-red-400 text-xs break-all">
|
||||||
|
{boxStatus.connector_error}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{boxStatus && (
|
||||||
|
<div className="text-muted-foreground text-xs space-y-0.5">
|
||||||
|
{boxStatus.backend && (
|
||||||
|
<p>
|
||||||
|
{t('monitoring.boxBackend')}:{' '}
|
||||||
|
<span className="text-foreground font-mono">
|
||||||
|
{boxStatus.backend.name}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</p>
|
||||||
<div className="grid grid-cols-2 gap-x-4 gap-y-1.5 text-xs">
|
)}
|
||||||
<div className="flex items-center gap-1.5 text-muted-foreground">
|
<p>
|
||||||
<Image className="w-3 h-3 flex-shrink-0" />
|
{t('monitoring.boxProfile')}:{' '}
|
||||||
<span className="text-foreground font-mono truncate">
|
<span className="text-foreground font-mono">
|
||||||
{session.image}
|
{boxStatus.profile}
|
||||||
</span>
|
</span>
|
||||||
|
</p>
|
||||||
|
{boxOk && boxStatus.active_sessions !== undefined && (
|
||||||
|
<p>
|
||||||
|
{t('monitoring.boxSandboxes')}:{' '}
|
||||||
|
<span className="text-foreground font-mono">
|
||||||
|
{boxStatus.active_sessions}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Active Sandboxes */}
|
||||||
|
{boxSessions.length > 0 && (
|
||||||
|
<div className="mt-3 space-y-2">
|
||||||
|
{boxSessions.map((session) => (
|
||||||
|
<div
|
||||||
|
key={session.session_id}
|
||||||
|
className="rounded-lg border p-3 space-y-2"
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-1.5 min-w-0">
|
||||||
|
<Container className="w-4 h-4 text-muted-foreground flex-shrink-0" />
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<span className="font-mono font-semibold text-foreground truncate text-sm">
|
||||||
|
{session.session_id}
|
||||||
|
</span>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
{session.session_id}
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1.5 text-muted-foreground">
|
<div className="grid grid-cols-2 gap-x-4 gap-y-1.5 text-xs">
|
||||||
<HardDrive className="w-3 h-3 flex-shrink-0" />
|
<div className="flex items-center gap-1.5 text-muted-foreground min-w-0">
|
||||||
<span className="text-foreground">
|
<Image className="w-3 h-3 flex-shrink-0" />
|
||||||
{session.backend_name}
|
<Tooltip>
|
||||||
</span>
|
<TooltipTrigger asChild>
|
||||||
</div>
|
<span className="text-foreground font-mono truncate">
|
||||||
<div className="flex items-center gap-1.5 text-muted-foreground">
|
{session.image}
|
||||||
<Cpu className="w-3 h-3 flex-shrink-0" />
|
</span>
|
||||||
<span className="text-foreground">
|
</TooltipTrigger>
|
||||||
{session.cpus} CPU / {session.memory_mb} MB
|
<TooltipContent>{session.image}</TooltipContent>
|
||||||
</span>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1.5 text-muted-foreground">
|
<div className="flex items-center gap-1.5 text-muted-foreground">
|
||||||
<Network className="w-3 h-3 flex-shrink-0" />
|
<HardDrive className="w-3 h-3 flex-shrink-0" />
|
||||||
<span className="text-foreground">
|
<span className="text-foreground">
|
||||||
{session.network}
|
{session.backend_name}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{session.host_path && (
|
<div className="flex items-center gap-1.5 text-muted-foreground">
|
||||||
<div className="flex items-center gap-1.5 text-muted-foreground col-span-2">
|
<Cpu className="w-3 h-3 flex-shrink-0" />
|
||||||
<FolderOpen className="w-3 h-3 flex-shrink-0" />
|
<span className="text-foreground">
|
||||||
<span
|
{session.cpus} CPU / {session.memory_mb} MB
|
||||||
className="text-foreground font-mono truncate"
|
</span>
|
||||||
title={session.host_path}
|
</div>
|
||||||
>
|
<div className="flex items-center gap-1.5 text-muted-foreground">
|
||||||
{session.mount_path}{' '}
|
<Network className="w-3 h-3 flex-shrink-0" />
|
||||||
<span className="text-muted-foreground">
|
<span className="text-foreground">
|
||||||
({session.host_path_mode})
|
{session.network}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{session.host_path && (
|
||||||
|
<div className="flex items-center gap-1.5 text-muted-foreground col-span-2 min-w-0">
|
||||||
|
<FolderOpen className="w-3 h-3 flex-shrink-0" />
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<span className="text-foreground font-mono truncate">
|
||||||
|
{session.host_path} : {session.mount_path}{' '}
|
||||||
|
<span className="text-muted-foreground">
|
||||||
|
({session.host_path_mode})
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
{session.host_path} : {session.mount_path} (
|
||||||
|
{session.host_path_mode})
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="flex items-center gap-1.5 text-muted-foreground">
|
||||||
|
<Clock className="w-3 h-3 flex-shrink-0" />
|
||||||
|
<span>
|
||||||
|
{t('monitoring.boxSessionCreated')}:{' '}
|
||||||
|
<span className="text-foreground">
|
||||||
|
{new Date(
|
||||||
|
session.created_at,
|
||||||
|
).toLocaleString()}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
<div className="flex items-center gap-1.5 text-muted-foreground">
|
||||||
<div className="flex items-center gap-1.5 text-muted-foreground">
|
<Clock className="w-3 h-3 flex-shrink-0" />
|
||||||
<Clock className="w-3 h-3 flex-shrink-0" />
|
<span>
|
||||||
<span>
|
{t('monitoring.boxSessionLastUsed')}:{' '}
|
||||||
{t('monitoring.boxSessionCreated')}:{' '}
|
<span className="text-foreground">
|
||||||
<span className="text-foreground">
|
{new Date(
|
||||||
{new Date(session.created_at).toLocaleString()}
|
session.last_used_at,
|
||||||
|
).toLocaleString()}
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</div>
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-1.5 text-muted-foreground">
|
|
||||||
<Clock className="w-3 h-3 flex-shrink-0" />
|
|
||||||
<span>
|
|
||||||
{t('monitoring.boxSessionLastUsed')}:{' '}
|
|
||||||
<span className="text-foreground">
|
|
||||||
{new Date(
|
|
||||||
session.last_used_at,
|
|
||||||
).toLocaleString()}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
))}
|
||||||
))}
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</TooltipProvider>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</>
|
</>
|
||||||
|
|||||||
Reference in New Issue
Block a user