refactor: Simplify ServerCard component layout and styles for improved readability and user experience, including badge updates and streamlined conditional rendering.
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m43s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m43s
This commit is contained in:
@@ -9,8 +9,7 @@ import {
|
||||
IconNetwork,
|
||||
IconCheck,
|
||||
IconCircleCheck,
|
||||
IconExternalLink,
|
||||
IconRefresh
|
||||
IconExternalLink
|
||||
} from '@tabler/icons-react';
|
||||
import { useState } from 'react';
|
||||
|
||||
@@ -51,30 +50,22 @@ export default function ServerCard({
|
||||
|
||||
return (
|
||||
<div
|
||||
className="server-row mb-2"
|
||||
className={`server-row card mb-2 ${selected ? 'border-primary border-2' : ''}`}
|
||||
style={{
|
||||
borderRadius: '10px',
|
||||
overflow: 'hidden',
|
||||
background: selected ? 'rgba(32, 107, 196, 0.04)' : '#fff',
|
||||
border: selected ? '2px solid #206bc4' : '1px solid rgba(0,0,0,0.08)',
|
||||
transition: 'all 0.15s ease',
|
||||
boxShadow: '0 1px 3px rgba(0,0,0,0.04)'
|
||||
transition: 'all 0.15s ease'
|
||||
}}
|
||||
>
|
||||
{/* Основная строка - всё в одну линию */}
|
||||
{/* Основная строка */}
|
||||
<div
|
||||
className="d-flex align-items-center px-3"
|
||||
style={{ height: '52px' }}
|
||||
>
|
||||
{/* Статус онлайн */}
|
||||
<div
|
||||
className="rounded-circle flex-shrink-0"
|
||||
style={{
|
||||
width: 10,
|
||||
height: 10,
|
||||
background: '#22c55e',
|
||||
marginRight: '12px'
|
||||
}}
|
||||
className="rounded-circle bg-success flex-shrink-0"
|
||||
style={{ width: 10, height: 10, marginRight: '12px' }}
|
||||
title="Онлайн"
|
||||
/>
|
||||
|
||||
@@ -99,24 +90,13 @@ export default function ServerCard({
|
||||
</button>
|
||||
|
||||
{/* Количество соединений */}
|
||||
<div
|
||||
className="d-flex align-items-center justify-content-center flex-shrink-0 me-3"
|
||||
style={{
|
||||
background: 'rgba(32, 107, 196, 0.08)',
|
||||
borderRadius: '6px',
|
||||
padding: '3px 8px',
|
||||
minWidth: '38px'
|
||||
}}
|
||||
>
|
||||
<IconNetwork size={13} className="text-primary" style={{ marginRight: 3 }} />
|
||||
<span className="fw-bold text-primary" style={{ fontSize: '0.75rem' }}>{connectionsCount}</span>
|
||||
</div>
|
||||
<span className="badge bg-green-lt text-green me-3 flex-shrink-0">
|
||||
<IconNetwork size={12} className="me-1" />
|
||||
{connectionsCount}
|
||||
</span>
|
||||
|
||||
{/* Флаг */}
|
||||
<span
|
||||
className="flex-shrink-0 me-2"
|
||||
style={{ fontSize: '1.15rem', lineHeight: 1 }}
|
||||
>
|
||||
{/* Флаг + Страна */}
|
||||
<span className="me-2 flex-shrink-0" style={{ fontSize: '1.1rem' }}>
|
||||
{countryToFlag(server.country)}
|
||||
</span>
|
||||
|
||||
@@ -125,8 +105,8 @@ export default function ServerCard({
|
||||
className="fw-semibold me-3 flex-shrink-0"
|
||||
style={{
|
||||
fontSize: '0.9rem',
|
||||
minWidth: '100px',
|
||||
maxWidth: '140px',
|
||||
minWidth: '80px',
|
||||
maxWidth: '120px',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap'
|
||||
@@ -135,47 +115,18 @@ export default function ServerCard({
|
||||
{shortName}
|
||||
</span>
|
||||
|
||||
{/* Тип */}
|
||||
<span
|
||||
className={`badge flex-shrink-0 me-3 ${isExit ? 'bg-danger' : 'bg-primary'}`}
|
||||
style={{
|
||||
fontSize: '0.65rem',
|
||||
padding: '3px 8px',
|
||||
borderRadius: '4px',
|
||||
fontWeight: 600,
|
||||
letterSpacing: '0.3px'
|
||||
}}
|
||||
>
|
||||
{/* Тип сервера */}
|
||||
<span className={`badge me-3 flex-shrink-0 ${isExit ? 'bg-red-lt text-red' : 'bg-blue-lt text-blue'}`}>
|
||||
{isExit ? 'EXIT' : 'JUMP'}
|
||||
</span>
|
||||
|
||||
{/* Провайдер */}
|
||||
<span
|
||||
className="badge flex-shrink-0 me-3"
|
||||
style={{
|
||||
fontSize: '0.7rem',
|
||||
padding: '4px 8px',
|
||||
borderRadius: '4px',
|
||||
background: 'rgba(0,0,0,0.06)',
|
||||
color: '#374151',
|
||||
fontWeight: 500
|
||||
}}
|
||||
>
|
||||
<span className="badge bg-secondary-lt text-secondary me-3 flex-shrink-0">
|
||||
{server.provider}
|
||||
</span>
|
||||
|
||||
{/* Туннель */}
|
||||
<span
|
||||
className="badge flex-shrink-0 me-3"
|
||||
style={{
|
||||
fontSize: '0.65rem',
|
||||
padding: '3px 6px',
|
||||
borderRadius: '4px',
|
||||
background: 'rgba(20, 184, 166, 0.12)',
|
||||
color: '#0d9488',
|
||||
fontWeight: 500
|
||||
}}
|
||||
>
|
||||
<span className="badge bg-teal-lt text-teal me-3 flex-shrink-0">
|
||||
{server.tunnel}
|
||||
</span>
|
||||
|
||||
@@ -197,18 +148,8 @@ export default function ServerCard({
|
||||
|
||||
{/* Gateway */}
|
||||
{primaryGateway && (
|
||||
<span
|
||||
className="badge flex-shrink-0 me-3"
|
||||
style={{
|
||||
fontSize: '0.7rem',
|
||||
padding: '4px 8px',
|
||||
borderRadius: '4px',
|
||||
background: 'rgba(34, 197, 94, 0.1)',
|
||||
color: '#16a34a',
|
||||
fontWeight: 500
|
||||
}}
|
||||
>
|
||||
<IconServer size={11} className="me-1" style={{ marginTop: -1 }} />
|
||||
<span className="badge bg-cyan-lt text-cyan me-3 flex-shrink-0">
|
||||
<IconServer size={11} className="me-1" />
|
||||
{primaryGateway}
|
||||
</span>
|
||||
)}
|
||||
@@ -216,90 +157,71 @@ export default function ServerCard({
|
||||
{/* Действия */}
|
||||
<div className="d-flex align-items-center gap-1 flex-shrink-0">
|
||||
<button
|
||||
className="btn btn-ghost-primary btn-sm p-1"
|
||||
className="btn btn-ghost-primary btn-icon btn-sm"
|
||||
title="Скопировать ссылку"
|
||||
onClick={(e) => { e.stopPropagation(); onQuickCopy?.(server); }}
|
||||
style={{ width: 28, height: 28, borderRadius: '6px' }}
|
||||
>
|
||||
<IconCopy size={15} />
|
||||
<IconCopy size={16} />
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-ghost-secondary btn-sm p-1"
|
||||
className="btn btn-ghost-secondary btn-icon btn-sm"
|
||||
title="Генератор ссылок"
|
||||
onClick={(e) => { e.stopPropagation(); onGenerateLink?.(server); }}
|
||||
style={{ width: 28, height: 28, borderRadius: '6px' }}
|
||||
>
|
||||
<IconLink size={15} />
|
||||
<IconLink size={16} />
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-ghost-secondary btn-sm p-1"
|
||||
className="btn btn-ghost-secondary btn-icon btn-sm"
|
||||
title="Редактировать"
|
||||
onClick={(e) => { e.stopPropagation(); onEdit?.(server); }}
|
||||
style={{ width: 28, height: 28, borderRadius: '6px' }}
|
||||
>
|
||||
<IconEdit size={15} />
|
||||
<IconEdit size={16} />
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-ghost-danger btn-sm p-1"
|
||||
className="btn btn-ghost-danger btn-icon btn-sm"
|
||||
title="Удалить"
|
||||
onClick={(e) => { e.stopPropagation(); onDelete?.(server); }}
|
||||
style={{ width: 28, height: 28, borderRadius: '6px' }}
|
||||
>
|
||||
<IconTrash size={15} />
|
||||
<IconTrash size={16} />
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-ghost-secondary btn-sm p-1"
|
||||
className="btn btn-ghost-secondary btn-icon btn-sm"
|
||||
onClick={() => setExpanded(!expanded)}
|
||||
title={expanded ? 'Свернуть' : 'Развернуть'}
|
||||
style={{ width: 28, height: 28, borderRadius: '6px' }}
|
||||
>
|
||||
{expanded ? <IconChevronUp size={15} /> : <IconChevronDown size={15} />}
|
||||
{expanded ? <IconChevronUp size={16} /> : <IconChevronDown size={16} />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Расширенная информация */}
|
||||
{expanded && (
|
||||
<div
|
||||
className="px-4 py-3"
|
||||
style={{
|
||||
background: 'rgba(0,0,0,0.015)',
|
||||
borderTop: '1px solid rgba(0,0,0,0.06)'
|
||||
}}
|
||||
>
|
||||
<div className="row g-3">
|
||||
<div className="card-body pt-0 border-top">
|
||||
<div className="row g-3 pt-3">
|
||||
<div className="col-md-3">
|
||||
<div className="text-muted small mb-1" style={{ fontSize: '0.7rem', textTransform: 'uppercase', letterSpacing: '0.5px' }}>IP адрес</div>
|
||||
<code style={{ fontSize: '0.85rem' }}>{server.ip}</code>
|
||||
<div className="text-muted small mb-1">IP адрес</div>
|
||||
<code>{server.ip}</code>
|
||||
</div>
|
||||
<div className="col-md-3">
|
||||
<div className="text-muted small mb-1" style={{ fontSize: '0.7rem', textTransform: 'uppercase', letterSpacing: '0.5px' }}>Внешний IP</div>
|
||||
<code style={{ fontSize: '0.85rem' }}>{server.extIp || '—'}</code>
|
||||
<div className="text-muted small mb-1">Внешний IP</div>
|
||||
<code>{server.extIp || '—'}</code>
|
||||
</div>
|
||||
<div className="col-md-3">
|
||||
<div className="text-muted small mb-1" style={{ fontSize: '0.7rem', textTransform: 'uppercase', letterSpacing: '0.5px' }}>Внутренний IP</div>
|
||||
<code style={{ fontSize: '0.85rem' }}>{server.internalIp || '—'}</code>
|
||||
<div className="text-muted small mb-1">Внутренний IP</div>
|
||||
<code>{server.internalIp || '—'}</code>
|
||||
</div>
|
||||
<div className="col-md-3">
|
||||
<div className="text-muted small mb-1" style={{ fontSize: '0.7rem', textTransform: 'uppercase', letterSpacing: '0.5px' }}>Страна</div>
|
||||
<span style={{ fontSize: '0.85rem' }}>{countryToFlag(server.country)} {server.country}</span>
|
||||
<div className="text-muted small mb-1">Страна</div>
|
||||
<span>{countryToFlag(server.country)} {server.country}</span>
|
||||
</div>
|
||||
{server.gateways?.length > 0 && (
|
||||
<div className="col-12">
|
||||
<div className="text-muted small mb-2" style={{ fontSize: '0.7rem', textTransform: 'uppercase', letterSpacing: '0.5px' }}>
|
||||
Шлюзы ({server.gateways.length})
|
||||
</div>
|
||||
<div className="text-muted small mb-2">Шлюзы ({server.gateways.length})</div>
|
||||
<div className="d-flex flex-wrap gap-2">
|
||||
{server.gateways.map((gw, idx) => (
|
||||
<span
|
||||
key={gw.id || idx}
|
||||
className={`badge ${gw.primary ? 'bg-primary' : ''}`}
|
||||
style={{
|
||||
fontSize: '0.75rem',
|
||||
padding: '5px 10px',
|
||||
background: gw.primary ? undefined : 'rgba(0,0,0,0.06)',
|
||||
color: gw.primary ? undefined : '#374151'
|
||||
}}
|
||||
className={`badge ${gw.primary ? 'bg-primary' : 'bg-secondary-lt text-secondary'}`}
|
||||
title={gw.comment || (gw.primary ? 'Основной' : 'Резервный')}
|
||||
>
|
||||
{gw.primary && <IconCheck size={11} className="me-1" />}
|
||||
|
||||
Reference in New Issue
Block a user