Skip to content

Commit f52b05c

Browse files
committed
chore(node): simplify card ui
1 parent 8326412 commit f52b05c

File tree

1 file changed

+36
-54
lines changed

1 file changed

+36
-54
lines changed

dashboard/src/components/nodes/node.tsx

Lines changed: 36 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@ import { useState } from 'react'
22
import { Card } from '../ui/card'
33
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, DropdownMenuSeparator } from '../ui/dropdown-menu'
44
import { Button } from '../ui/button'
5-
import { MoreVertical, Pencil, Trash2, Power, Activity, RotateCcw, Wifi, Loader2, RefreshCw, Download, Package, Server, AlertCircle, CheckCircle2, Clock, XCircle, Link2, Map } from 'lucide-react'
5+
import { MoreVertical, Pencil, Trash2, Power, Activity, RotateCcw, Wifi, Loader2, RefreshCw, Download, Package, Server, AlertCircle, Link2, Map } from 'lucide-react'
66
import { useTranslation } from 'react-i18next'
77
import useDirDetection from '@/hooks/use-dir-detection'
88
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from '@/components/ui/alert-dialog'
99
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
10-
import { Badge } from '@/components/ui/badge'
1110
import { Separator } from '@/components/ui/separator'
1211
import { cn } from '@/lib/utils'
1312
import { toast } from 'sonner'
@@ -213,56 +212,60 @@ export default function Node({ node, onEdit, onToggleStatus }: NodeProps) {
213212
switch (node.status) {
214213
case 'connected':
215214
return {
216-
icon: CheckCircle2,
217215
label: t('nodeModal.status.connected', { defaultValue: 'Connected' }),
218-
className: 'bg-emerald-500/10 text-emerald-700 dark:text-emerald-400 border-emerald-500/20',
219-
dotColor: 'bg-emerald-500',
220216
}
221217
case 'connecting':
222218
return {
223-
icon: Clock,
224219
label: t('nodeModal.status.connecting', { defaultValue: 'Connecting' }),
225-
className: 'bg-amber-500/10 text-amber-700 dark:text-amber-400 border-amber-500/20',
226-
dotColor: 'bg-amber-500',
227220
}
228221
case 'error':
229222
return {
230-
icon: XCircle,
231223
label: t('nodeModal.status.error', { defaultValue: 'Error' }),
232-
className: 'bg-destructive/10 text-destructive border-destructive/20',
233-
dotColor: 'bg-destructive',
234224
}
235225
case 'limited':
236226
return {
237-
icon: AlertCircle,
238227
label: t('status.limited', { defaultValue: 'Limited' }),
239-
className: 'bg-orange-500/10 text-orange-700 dark:text-orange-400 border-orange-500/20',
240-
dotColor: 'bg-orange-500',
241228
}
242229
default:
243230
return {
244-
icon: XCircle,
245231
label: t('nodeModal.status.disabled', { defaultValue: 'Disabled' }),
246-
className: 'bg-muted text-muted-foreground border-border',
247-
dotColor: 'bg-muted-foreground/50',
248232
}
249233
}
250234
}
251235

252236
const statusConfig = getStatusConfig()
253-
const StatusIcon = statusConfig.icon
237+
238+
const getStatusDotColor = () => {
239+
switch (node.status) {
240+
case 'connected':
241+
return 'bg-green-500'
242+
case 'connecting':
243+
return 'bg-amber-500'
244+
case 'error':
245+
return 'bg-destructive'
246+
case 'limited':
247+
return 'bg-orange-500'
248+
default:
249+
return 'bg-gray-400 dark:bg-gray-600'
250+
}
251+
}
254252

255253
return (
256254
<TooltipProvider>
257255
<Card className="group relative h-full cursor-pointer overflow-hidden border transition-colors hover:bg-accent" onClick={() => onEdit(node)}>
258-
{/* Status accent bar */}
259-
<div className={cn('absolute inset-x-0 top-0 h-0.5 sm:h-1', statusConfig.dotColor)} />
260-
261256
<div className="p-3">
262257
{/* Header */}
263-
<div className="mb-2 flex items-start justify-between gap-2">
258+
<div className="flex items-start justify-between gap-2">
264259
<div className="min-w-0 flex-1">
265-
<div className="mb-1.5 flex items-center gap-1.5">
260+
<div className="mb-0.5 flex items-center gap-1.5">
261+
<Tooltip>
262+
<TooltipTrigger asChild>
263+
<div className={cn('h-2 w-2 rounded-full shrink-0', getStatusDotColor())} />
264+
</TooltipTrigger>
265+
<TooltipContent>
266+
<p>{statusConfig.label}</p>
267+
</TooltipContent>
268+
</Tooltip>
266269
<h3 className="truncate text-sm sm:text-base font-semibold leading-tight tracking-tight">{node.name}</h3>
267270
{node.status === 'error' && node.message ? (
268271
<Tooltip>
@@ -275,27 +278,6 @@ export default function Node({ node, onEdit, onToggleStatus }: NodeProps) {
275278
</Tooltip>
276279
) : null}
277280
</div>
278-
<div className="flex items-center gap-1.5">
279-
<Tooltip>
280-
<TooltipTrigger asChild>
281-
<Badge variant="outline" className={cn('text-[9px] sm:text-[10px] font-medium transition-colors flex items-center gap-1', statusConfig.className)}>
282-
<StatusIcon className={cn('h-2.5 w-2.5 sm:h-3 sm:w-3 shrink-0', dir === 'rtl' ? 'ml-0.5' : 'mr-0.5')} />
283-
{statusConfig.label}
284-
</Badge>
285-
</TooltipTrigger>
286-
<TooltipContent className="max-w-xs">
287-
<div className="space-y-2 text-xs">
288-
<div className="font-semibold">{t('node.status', { defaultValue: 'Node Status' })}</div>
289-
<div className="space-y-1.5">
290-
<div className="flex items-center justify-between gap-4">
291-
<span>{t('status', { defaultValue: 'Status' })}</span>
292-
<span className="font-medium">{statusConfig.label}</span>
293-
</div>
294-
</div>
295-
</div>
296-
</TooltipContent>
297-
</Tooltip>
298-
</div>
299281
</div>
300282
<div onClick={e => e.stopPropagation()}>
301283
<DropdownMenu>
@@ -425,8 +407,8 @@ export default function Node({ node, onEdit, onToggleStatus }: NodeProps) {
425407
'group/version inline-flex items-center rounded-md border px-1.5 py-0.5 sm:px-2 sm:py-1 transition-all cursor-pointer',
426408
dir === 'rtl' ? 'flex-row-reverse gap-1' : 'gap-1',
427409
latestXrayVersion && hasXrayUpdate(node.xray_version)
428-
? 'border-amber-500/50 bg-amber-500/10 hover:border-amber-500/70 hover:bg-amber-500/15'
429-
: 'border-border/50 bg-muted/40 hover:border-border hover:bg-muted/60',
410+
? 'border-amber-500/50 bg-amber-500/10 group-hover:border-amber-500/70 group-hover:bg-amber-500/15'
411+
: 'border-border/50 bg-background/50 group-hover:border-border group-hover:bg-background/80',
430412
)}
431413
onClick={e => {
432414
e.stopPropagation()
@@ -438,9 +420,9 @@ export default function Node({ node, onEdit, onToggleStatus }: NodeProps) {
438420
{node.xray_version}
439421
</span>
440422
{latestXrayVersion && hasXrayUpdate(node.xray_version) && (
441-
<div className={cn('flex items-center', dir === 'rtl' ? 'flex-row-reverse gap-0.5' : 'gap-0.5')}>
442-
<div className="h-1 w-1 sm:h-1.5 sm:w-1.5 rounded-full bg-amber-500 animate-pulse" />
443-
<Download className="h-2.5 w-2.5 sm:h-3 sm:w-3 text-amber-600 dark:text-amber-400" />
423+
<div className={cn('flex items-center gap-0.5', dir === 'rtl' ? 'flex-row-reverse' : '')}>
424+
<div className="h-1.5 w-1.5 rounded-full bg-amber-500 shrink-0" />
425+
<Download className="h-2.5 w-2.5 text-amber-600 dark:text-amber-400 shrink-0" />
444426
</div>
445427
)}
446428
</div>
@@ -478,18 +460,18 @@ export default function Node({ node, onEdit, onToggleStatus }: NodeProps) {
478460
'group/version inline-flex items-center rounded-md border px-1.5 py-0.5 sm:px-2 sm:py-1 transition-all',
479461
dir === 'rtl' ? 'flex-row-reverse gap-1' : 'gap-1',
480462
latestNodeVersion && hasNodeUpdate(node.node_version)
481-
? 'border-amber-500/50 bg-amber-500/10'
482-
: 'border-border/50 bg-muted/40',
463+
? 'border-amber-500/50 bg-amber-500/10 group-hover:border-amber-500/70 group-hover:bg-amber-500/15'
464+
: 'border-border/50 bg-background/50 group-hover:border-border group-hover:bg-background/80',
483465
)}
484466
>
485467
<Server className={cn('h-3 w-3 sm:h-3.5 sm:w-3.5 shrink-0 transition-colors', latestNodeVersion && hasNodeUpdate(node.node_version) ? 'text-amber-600 dark:text-amber-400' : 'text-muted-foreground')} />
486468
<span className={cn('text-[10px] sm:text-[11px] font-medium font-mono', latestNodeVersion && hasNodeUpdate(node.node_version) ? 'text-amber-700 dark:text-amber-300' : 'text-foreground')}>
487469
{node.node_version}
488470
</span>
489471
{latestNodeVersion && hasNodeUpdate(node.node_version) && (
490-
<div className={cn('flex items-center', dir === 'rtl' ? 'flex-row-reverse gap-0.5' : 'gap-0.5')}>
491-
<div className="h-1 w-1 sm:h-1.5 sm:w-1.5 rounded-full bg-amber-500 animate-pulse" />
492-
<Download className="h-2.5 w-2.5 sm:h-3 sm:w-3 text-amber-600 dark:text-amber-400" />
472+
<div className={cn('flex items-center gap-0.5', dir === 'rtl' ? 'flex-row-reverse' : '')}>
473+
<div className="h-1.5 w-1.5 rounded-full bg-amber-500 shrink-0" />
474+
<Download className="h-2.5 w-2.5 text-amber-600 dark:text-amber-400 shrink-0" />
493475
</div>
494476
)}
495477
</div>

0 commit comments

Comments
 (0)