feat(EasySwitch): enhance Easy Switch UI with new styles and layout adjustments for improved user experience
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 2m5s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 2m5s
This commit is contained in:
@@ -697,6 +697,82 @@ body.sidebar-overlay-open {
|
|||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Easy Switch: bring visuals in line with Tabler pages */
|
||||||
|
.easy-switch-page .card-tabs .nav-link {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.easy-switch-server-header {
|
||||||
|
background-color: rgba(32, 107, 196, 0.05);
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.easy-switch-flag {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.easy-switch-gateway-card {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
|
||||||
|
border: 1px solid var(--tblr-border-color);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.easy-switch-gateway-card:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 0.125rem 0.375rem rgba(32, 41, 57, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.easy-switch-gateway-card-active {
|
||||||
|
border: 1px solid var(--tblr-primary) !important;
|
||||||
|
box-shadow: 0 0.25rem 0.75rem rgba(32, 107, 196, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.easy-switch-active-check {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.easy-switch-fast-slot {
|
||||||
|
min-height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.easy-switch-fast-badge {
|
||||||
|
font-size: 0.65rem;
|
||||||
|
padding: 2px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.easy-switch-gateway-flag {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
line-height: 1;
|
||||||
|
background-color: rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.easy-switch-gateway-flag-active {
|
||||||
|
background-color: rgba(255, 255, 255, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.easy-switch-gateway-title {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
line-height: 1.2;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.easy-switch-gateway-ip {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.easy-switch-gateway-ping {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
/* Ripple effect для кнопок */
|
/* Ripple effect для кнопок */
|
||||||
@keyframes ripple {
|
@keyframes ripple {
|
||||||
to {
|
to {
|
||||||
|
|||||||
@@ -505,7 +505,7 @@ function EasySwitchManager() {
|
|||||||
|
|
||||||
if (loading && servers.length === 0) {
|
if (loading && servers.length === 0) {
|
||||||
return (
|
return (
|
||||||
<div className="container-xl">
|
<div className="easy-switch-page">
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title="Easy Switch"
|
title="Easy Switch"
|
||||||
icon={<IconWorld size={24} />}
|
icon={<IconWorld size={24} />}
|
||||||
@@ -525,7 +525,7 @@ function EasySwitchManager() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container-xl">
|
<div className="easy-switch-page">
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title="Easy Switch"
|
title="Easy Switch"
|
||||||
icon={<IconWorld size={24} />}
|
icon={<IconWorld size={24} />}
|
||||||
@@ -554,7 +554,7 @@ function EasySwitchManager() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Вкладки */}
|
{/* Вкладки */}
|
||||||
<div className="mb-3">
|
<div className="card card-tabs mb-3">
|
||||||
<ul className="nav nav-tabs" role="tablist">
|
<ul className="nav nav-tabs" role="tablist">
|
||||||
<li className="nav-item" role="presentation">
|
<li className="nav-item" role="presentation">
|
||||||
<button
|
<button
|
||||||
@@ -599,8 +599,9 @@ function EasySwitchManager() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Панель фильтров */}
|
{/* Панель фильтров */}
|
||||||
<div className="mb-3">
|
<div className="card mb-3">
|
||||||
<div className="row g-2 align-items-center">
|
<div className="card-body py-3">
|
||||||
|
<div className="row g-2 align-items-center">
|
||||||
{/* Переключатель "Только настроенные" */}
|
{/* Переключатель "Только настроенные" */}
|
||||||
<div className="col-auto">
|
<div className="col-auto">
|
||||||
<label className="form-check form-switch mb-0">
|
<label className="form-check form-switch mb-0">
|
||||||
@@ -679,17 +680,18 @@ function EasySwitchManager() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-auto">
|
<div className="col-auto">
|
||||||
<button className="btn btn-icon" title="Настройки">
|
<button className="btn btn-outline-secondary btn-icon" title="Настройки">
|
||||||
<IconSettings size={18} />
|
<IconSettings size={18} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-auto">
|
<div className="col-auto">
|
||||||
<button className="btn btn-icon" title="Быстрые действия">
|
<button className="btn btn-outline-secondary btn-icon" title="Быстрые действия">
|
||||||
<IconFilter size={18} />
|
<IconFilter size={18} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Список серверов с communities и шлюзами */}
|
{/* Список серверов с communities и шлюзами */}
|
||||||
{filteredServers.length === 0 ? (
|
{filteredServers.length === 0 ? (
|
||||||
@@ -726,12 +728,7 @@ function EasySwitchManager() {
|
|||||||
<div key={server.id} className="card mb-3">
|
<div key={server.id} className="card mb-3">
|
||||||
{/* Заголовок сервера (кликабельный) */}
|
{/* Заголовок сервера (кликабельный) */}
|
||||||
<div
|
<div
|
||||||
className="card-header py-2"
|
className="card-header py-2 easy-switch-server-header"
|
||||||
style={{
|
|
||||||
backgroundColor: 'rgba(32, 107, 196, 0.05)',
|
|
||||||
cursor: 'pointer',
|
|
||||||
userSelect: 'none'
|
|
||||||
}}
|
|
||||||
onClick={() => toggleServerExpanded(server.id)}
|
onClick={() => toggleServerExpanded(server.id)}
|
||||||
>
|
>
|
||||||
<div className="d-flex align-items-center">
|
<div className="d-flex align-items-center">
|
||||||
@@ -742,7 +739,7 @@ function EasySwitchManager() {
|
|||||||
<IconChevronDown size={20} className="me-2 text-muted" />
|
<IconChevronDown size={20} className="me-2 text-muted" />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{flag && <span className="me-2" style={{ fontSize: '1.5rem' }}>{flag}</span>}
|
{flag && <span className="me-2 easy-switch-flag">{flag}</span>}
|
||||||
<IconServer size={20} className="me-2 text-primary" />
|
<IconServer size={20} className="me-2 text-primary" />
|
||||||
<h3 className="card-title mb-0 me-2">{server.name}</h3>
|
<h3 className="card-title mb-0 me-2">{server.name}</h3>
|
||||||
{server.description && (
|
{server.description && (
|
||||||
@@ -793,7 +790,7 @@ function EasySwitchManager() {
|
|||||||
<div key={tag} className="mb-4">
|
<div key={tag} className="mb-4">
|
||||||
{/* Заголовок группы тегов */}
|
{/* Заголовок группы тегов */}
|
||||||
<div className="mb-2 pb-2 border-bottom">
|
<div className="mb-2 pb-2 border-bottom">
|
||||||
<span className="badge bg-purple-lt text-purple me-2" style={{ fontSize: '0.85rem', padding: '6px 10px' }}>
|
<span className="badge bg-purple-lt text-purple me-2">
|
||||||
{tag}
|
{tag}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-muted small">
|
<span className="text-muted small">
|
||||||
@@ -813,12 +810,12 @@ function EasySwitchManager() {
|
|||||||
<div className="mb-2">
|
<div className="mb-2">
|
||||||
{/* Заголовок community */}
|
{/* Заголовок community */}
|
||||||
<div className="mb-2">
|
<div className="mb-2">
|
||||||
<span className="badge bg-blue-lt text-blue me-2" style={{ fontSize: '0.8rem' }}>
|
<span className="badge bg-blue-lt text-blue me-2">
|
||||||
{comm.value}
|
{comm.value}
|
||||||
</span>
|
</span>
|
||||||
<span className="fw-medium">{comm.name || comm.description || ''}</span>
|
<span className="fw-medium">{comm.name || comm.description || ''}</span>
|
||||||
{hasFilterForComm && (
|
{hasFilterForComm && (
|
||||||
<span className="badge bg-green-lt text-green ms-2" style={{ fontSize: '0.7rem' }}>
|
<span className="badge bg-green-lt text-green ms-2">
|
||||||
настроен
|
настроен
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
@@ -842,50 +839,21 @@ function EasySwitchManager() {
|
|||||||
return (
|
return (
|
||||||
<div key={gw.name} className="col-12 col-sm-4">
|
<div key={gw.name} className="col-12 col-sm-4">
|
||||||
<div
|
<div
|
||||||
className={`card cursor-pointer h-100 ${isActive ? 'bg-primary text-white' : ''}`}
|
className={`card h-100 easy-switch-gateway-card ${isActive ? 'easy-switch-gateway-card-active bg-primary text-white' : ''}`}
|
||||||
style={{
|
|
||||||
cursor: 'pointer',
|
|
||||||
transition: 'all 0.15s ease',
|
|
||||||
border: isActive ? '2px solid var(--tblr-primary)' : '1px solid rgba(0,0,0,0.1)',
|
|
||||||
borderRadius: '6px',
|
|
||||||
boxShadow: isActive ? '0 3px 8px rgba(32, 107, 196, 0.3)' : 'none',
|
|
||||||
position: 'relative'
|
|
||||||
}}
|
|
||||||
onClick={() => handleGatewaySelect(server.id, comm.value, gw.name)}
|
onClick={() => handleGatewaySelect(server.id, comm.value, gw.name)}
|
||||||
onMouseEnter={(e) => {
|
|
||||||
if (!isActive) {
|
|
||||||
e.currentTarget.style.boxShadow = '0 2px 6px rgba(0,0,0,0.1)';
|
|
||||||
e.currentTarget.style.transform = 'translateY(-1px)';
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onMouseLeave={(e) => {
|
|
||||||
if (!isActive) {
|
|
||||||
e.currentTarget.style.boxShadow = 'none';
|
|
||||||
e.currentTarget.style.transform = 'translateY(0)';
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<div className="card-body p-2">
|
<div className="card-body p-2">
|
||||||
{/* Галочка (абсолютное позиционирование) */}
|
{/* Галочка (абсолютное позиционирование) */}
|
||||||
{isActive && (
|
{isActive && (
|
||||||
<div
|
<div className="bg-white text-primary rounded-circle d-flex align-items-center justify-content-center easy-switch-active-check">
|
||||||
className="bg-white text-primary rounded-circle d-flex align-items-center justify-content-center"
|
|
||||||
style={{
|
|
||||||
width: '20px',
|
|
||||||
height: '20px',
|
|
||||||
position: 'absolute',
|
|
||||||
top: '8px',
|
|
||||||
right: '8px'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<IconCheck size={12} />
|
<IconCheck size={12} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Метка Fastest */}
|
{/* Метка Fastest */}
|
||||||
<div className="mb-1" style={{ minHeight: '18px' }}>
|
<div className="mb-1 easy-switch-fast-slot">
|
||||||
{isFastest && !isActive && (
|
{isFastest && !isActive && (
|
||||||
<span className="badge bg-yellow-lt text-yellow d-inline-flex align-items-center" style={{ fontSize: '0.65rem', padding: '2px 6px' }}>
|
<span className="badge bg-yellow-lt text-yellow d-inline-flex align-items-center easy-switch-fast-badge">
|
||||||
<IconBolt size={10} className="me-1" />
|
<IconBolt size={10} className="me-1" />
|
||||||
Fast
|
Fast
|
||||||
</span>
|
</span>
|
||||||
@@ -895,22 +863,14 @@ function EasySwitchManager() {
|
|||||||
{/* Флаг и название (комментарий) шлюза */}
|
{/* Флаг и название (комментарий) шлюза */}
|
||||||
<div className="d-flex align-items-center gap-2 mb-1">
|
<div className="d-flex align-items-center gap-2 mb-1">
|
||||||
<span
|
<span
|
||||||
className="d-flex align-items-center justify-content-center flex-shrink-0 rounded"
|
className={`d-flex align-items-center justify-content-center flex-shrink-0 rounded easy-switch-gateway-flag ${isActive ? 'easy-switch-gateway-flag-active' : ''}`}
|
||||||
style={{
|
|
||||||
width: '28px',
|
|
||||||
height: '28px',
|
|
||||||
fontSize: '1.25rem',
|
|
||||||
lineHeight: 1,
|
|
||||||
backgroundColor: isActive ? 'rgba(255,255,255,0.25)' : 'rgba(0,0,0,0.06)',
|
|
||||||
}}
|
|
||||||
title={gw.country || country || ''}
|
title={gw.country || country || ''}
|
||||||
>
|
>
|
||||||
{countryToFlag(gw.country || country || '') || '🌐'}
|
{countryToFlag(gw.country || country || '') || '🌐'}
|
||||||
</span>
|
</span>
|
||||||
<div className="flex-grow-1 min-w-0">
|
<div className="flex-grow-1 min-w-0">
|
||||||
<div
|
<div
|
||||||
className={`fw-bold ${isActive ? 'text-white' : ''}`}
|
className={`fw-bold easy-switch-gateway-title ${isActive ? 'text-white' : ''}`}
|
||||||
style={{ fontSize: '0.85rem', lineHeight: '1.2', wordBreak: 'break-word' }}
|
|
||||||
>
|
>
|
||||||
{gw.comment || gw.name}
|
{gw.comment || gw.name}
|
||||||
</div>
|
</div>
|
||||||
@@ -919,17 +879,16 @@ function EasySwitchManager() {
|
|||||||
{/* IP шлюза ниже */}
|
{/* IP шлюза ниже */}
|
||||||
{gw.ip && (
|
{gw.ip && (
|
||||||
<div
|
<div
|
||||||
className={`small text-truncate ${
|
className={`small text-truncate easy-switch-gateway-ip ${
|
||||||
isActive ? 'text-white-75' : 'text-muted'
|
isActive ? 'text-white-75' : 'text-muted'
|
||||||
}`}
|
}`}
|
||||||
style={{ fontSize: '0.75rem' }}
|
|
||||||
>
|
>
|
||||||
{gw.ip}
|
{gw.ip}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Метрика (пинг) */}
|
{/* Метрика (пинг) */}
|
||||||
<div className={`fw-bold mt-1 ${isActive ? 'text-white' : 'text-success'}`} style={{ fontSize: '1.1rem' }}>
|
<div className={`fw-bold mt-1 easy-switch-gateway-ping ${isActive ? 'text-white' : 'text-success'}`}>
|
||||||
{ping != null ? ping : '—'}
|
{ping != null ? ping : '—'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -952,7 +911,7 @@ function EasySwitchManager() {
|
|||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
{/* Заголовок группы без тегов */}
|
{/* Заголовок группы без тегов */}
|
||||||
<div className="mb-2 pb-2 border-bottom">
|
<div className="mb-2 pb-2 border-bottom">
|
||||||
<span className="badge bg-gray-lt text-gray me-2" style={{ fontSize: '0.85rem', padding: '6px 10px' }}>
|
<span className="badge bg-gray-lt text-gray me-2">
|
||||||
Без тегов
|
Без тегов
|
||||||
</span>
|
</span>
|
||||||
<span className="text-muted small">
|
<span className="text-muted small">
|
||||||
@@ -972,12 +931,12 @@ function EasySwitchManager() {
|
|||||||
<div className="mb-2">
|
<div className="mb-2">
|
||||||
{/* Заголовок community */}
|
{/* Заголовок community */}
|
||||||
<div className="mb-2">
|
<div className="mb-2">
|
||||||
<span className="badge bg-blue-lt text-blue me-2" style={{ fontSize: '0.8rem' }}>
|
<span className="badge bg-blue-lt text-blue me-2">
|
||||||
{comm.value}
|
{comm.value}
|
||||||
</span>
|
</span>
|
||||||
<span className="fw-medium">{comm.name || comm.description || ''}</span>
|
<span className="fw-medium">{comm.name || comm.description || ''}</span>
|
||||||
{hasFilterForComm && (
|
{hasFilterForComm && (
|
||||||
<span className="badge bg-green-lt text-green ms-2" style={{ fontSize: '0.7rem' }}>
|
<span className="badge bg-green-lt text-green ms-2">
|
||||||
настроен
|
настроен
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
@@ -1001,50 +960,21 @@ function EasySwitchManager() {
|
|||||||
return (
|
return (
|
||||||
<div key={gw.name} className="col-12 col-sm-4">
|
<div key={gw.name} className="col-12 col-sm-4">
|
||||||
<div
|
<div
|
||||||
className={`card cursor-pointer h-100 ${isActive ? 'bg-primary text-white' : ''}`}
|
className={`card h-100 easy-switch-gateway-card ${isActive ? 'easy-switch-gateway-card-active bg-primary text-white' : ''}`}
|
||||||
style={{
|
|
||||||
cursor: 'pointer',
|
|
||||||
transition: 'all 0.15s ease',
|
|
||||||
border: isActive ? '2px solid var(--tblr-primary)' : '1px solid rgba(0,0,0,0.1)',
|
|
||||||
borderRadius: '6px',
|
|
||||||
boxShadow: isActive ? '0 3px 8px rgba(32, 107, 196, 0.3)' : 'none',
|
|
||||||
position: 'relative'
|
|
||||||
}}
|
|
||||||
onClick={() => handleGatewaySelect(server.id, comm.value, gw.name)}
|
onClick={() => handleGatewaySelect(server.id, comm.value, gw.name)}
|
||||||
onMouseEnter={(e) => {
|
|
||||||
if (!isActive) {
|
|
||||||
e.currentTarget.style.boxShadow = '0 2px 6px rgba(0,0,0,0.1)';
|
|
||||||
e.currentTarget.style.transform = 'translateY(-1px)';
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onMouseLeave={(e) => {
|
|
||||||
if (!isActive) {
|
|
||||||
e.currentTarget.style.boxShadow = 'none';
|
|
||||||
e.currentTarget.style.transform = 'translateY(0)';
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<div className="card-body p-2">
|
<div className="card-body p-2">
|
||||||
{/* Галочка (абсолютное позиционирование) */}
|
{/* Галочка (абсолютное позиционирование) */}
|
||||||
{isActive && (
|
{isActive && (
|
||||||
<div
|
<div className="bg-white text-primary rounded-circle d-flex align-items-center justify-content-center easy-switch-active-check">
|
||||||
className="bg-white text-primary rounded-circle d-flex align-items-center justify-content-center"
|
|
||||||
style={{
|
|
||||||
width: '20px',
|
|
||||||
height: '20px',
|
|
||||||
position: 'absolute',
|
|
||||||
top: '8px',
|
|
||||||
right: '8px'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<IconCheck size={12} />
|
<IconCheck size={12} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Метка Fastest */}
|
{/* Метка Fastest */}
|
||||||
<div className="mb-1" style={{ minHeight: '18px' }}>
|
<div className="mb-1 easy-switch-fast-slot">
|
||||||
{isFastest && !isActive && (
|
{isFastest && !isActive && (
|
||||||
<span className="badge bg-yellow-lt text-yellow d-inline-flex align-items-center" style={{ fontSize: '0.65rem', padding: '2px 6px' }}>
|
<span className="badge bg-yellow-lt text-yellow d-inline-flex align-items-center easy-switch-fast-badge">
|
||||||
<IconBolt size={10} className="me-1" />
|
<IconBolt size={10} className="me-1" />
|
||||||
Fast
|
Fast
|
||||||
</span>
|
</span>
|
||||||
@@ -1054,22 +984,14 @@ function EasySwitchManager() {
|
|||||||
{/* Флаг и название (комментарий) шлюза */}
|
{/* Флаг и название (комментарий) шлюза */}
|
||||||
<div className="d-flex align-items-center gap-2 mb-1">
|
<div className="d-flex align-items-center gap-2 mb-1">
|
||||||
<span
|
<span
|
||||||
className="d-flex align-items-center justify-content-center flex-shrink-0 rounded"
|
className={`d-flex align-items-center justify-content-center flex-shrink-0 rounded easy-switch-gateway-flag ${isActive ? 'easy-switch-gateway-flag-active' : ''}`}
|
||||||
style={{
|
|
||||||
width: '28px',
|
|
||||||
height: '28px',
|
|
||||||
fontSize: '1.25rem',
|
|
||||||
lineHeight: 1,
|
|
||||||
backgroundColor: isActive ? 'rgba(255,255,255,0.25)' : 'rgba(0,0,0,0.06)',
|
|
||||||
}}
|
|
||||||
title={gw.country || country || ''}
|
title={gw.country || country || ''}
|
||||||
>
|
>
|
||||||
{countryToFlag(gw.country || country || '') || '🌐'}
|
{countryToFlag(gw.country || country || '') || '🌐'}
|
||||||
</span>
|
</span>
|
||||||
<div className="flex-grow-1 min-w-0">
|
<div className="flex-grow-1 min-w-0">
|
||||||
<div
|
<div
|
||||||
className={`fw-bold ${isActive ? 'text-white' : ''}`}
|
className={`fw-bold easy-switch-gateway-title ${isActive ? 'text-white' : ''}`}
|
||||||
style={{ fontSize: '0.85rem', lineHeight: '1.2', wordBreak: 'break-word' }}
|
|
||||||
>
|
>
|
||||||
{gw.comment || gw.name}
|
{gw.comment || gw.name}
|
||||||
</div>
|
</div>
|
||||||
@@ -1078,17 +1000,16 @@ function EasySwitchManager() {
|
|||||||
{/* IP шлюза ниже */}
|
{/* IP шлюза ниже */}
|
||||||
{gw.ip && (
|
{gw.ip && (
|
||||||
<div
|
<div
|
||||||
className={`small text-truncate ${
|
className={`small text-truncate easy-switch-gateway-ip ${
|
||||||
isActive ? 'text-white-75' : 'text-muted'
|
isActive ? 'text-white-75' : 'text-muted'
|
||||||
}`}
|
}`}
|
||||||
style={{ fontSize: '0.75rem' }}
|
|
||||||
>
|
>
|
||||||
{gw.ip}
|
{gw.ip}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Метрика (пинг) */}
|
{/* Метрика (пинг) */}
|
||||||
<div className={`fw-bold mt-1 ${isActive ? 'text-white' : 'text-success'}`} style={{ fontSize: '1.1rem' }}>
|
<div className={`fw-bold mt-1 easy-switch-gateway-ping ${isActive ? 'text-white' : 'text-success'}`}>
|
||||||
{ping != null ? ping : '—'}
|
{ping != null ? ping : '—'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1120,12 +1041,12 @@ function EasySwitchManager() {
|
|||||||
<div className="mb-2">
|
<div className="mb-2">
|
||||||
{/* Заголовок community */}
|
{/* Заголовок community */}
|
||||||
<div className="mb-2">
|
<div className="mb-2">
|
||||||
<span className="badge bg-blue-lt text-blue me-2" style={{ fontSize: '0.8rem' }}>
|
<span className="badge bg-blue-lt text-blue me-2">
|
||||||
{comm.value}
|
{comm.value}
|
||||||
</span>
|
</span>
|
||||||
<span className="fw-medium">{comm.name || comm.description || ''}</span>
|
<span className="fw-medium">{comm.name || comm.description || ''}</span>
|
||||||
{hasFilterForComm && (
|
{hasFilterForComm && (
|
||||||
<span className="badge bg-green-lt text-green ms-2" style={{ fontSize: '0.7rem' }}>
|
<span className="badge bg-green-lt text-green ms-2">
|
||||||
настроен
|
настроен
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
@@ -1149,50 +1070,21 @@ function EasySwitchManager() {
|
|||||||
return (
|
return (
|
||||||
<div key={gw.name} className="col-12 col-sm-4">
|
<div key={gw.name} className="col-12 col-sm-4">
|
||||||
<div
|
<div
|
||||||
className={`card cursor-pointer h-100 ${isActive ? 'bg-primary text-white' : ''}`}
|
className={`card h-100 easy-switch-gateway-card ${isActive ? 'easy-switch-gateway-card-active bg-primary text-white' : ''}`}
|
||||||
style={{
|
|
||||||
cursor: 'pointer',
|
|
||||||
transition: 'all 0.15s ease',
|
|
||||||
border: isActive ? '2px solid var(--tblr-primary)' : '1px solid rgba(0,0,0,0.1)',
|
|
||||||
borderRadius: '6px',
|
|
||||||
boxShadow: isActive ? '0 3px 8px rgba(32, 107, 196, 0.3)' : 'none',
|
|
||||||
position: 'relative'
|
|
||||||
}}
|
|
||||||
onClick={() => handleGatewaySelect(server.id, comm.value, gw.name)}
|
onClick={() => handleGatewaySelect(server.id, comm.value, gw.name)}
|
||||||
onMouseEnter={(e) => {
|
|
||||||
if (!isActive) {
|
|
||||||
e.currentTarget.style.boxShadow = '0 2px 6px rgba(0,0,0,0.1)';
|
|
||||||
e.currentTarget.style.transform = 'translateY(-1px)';
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onMouseLeave={(e) => {
|
|
||||||
if (!isActive) {
|
|
||||||
e.currentTarget.style.boxShadow = 'none';
|
|
||||||
e.currentTarget.style.transform = 'translateY(0)';
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<div className="card-body p-2">
|
<div className="card-body p-2">
|
||||||
{/* Галочка (абсолютное позиционирование) */}
|
{/* Галочка (абсолютное позиционирование) */}
|
||||||
{isActive && (
|
{isActive && (
|
||||||
<div
|
<div className="bg-white text-primary rounded-circle d-flex align-items-center justify-content-center easy-switch-active-check">
|
||||||
className="bg-white text-primary rounded-circle d-flex align-items-center justify-content-center"
|
|
||||||
style={{
|
|
||||||
width: '20px',
|
|
||||||
height: '20px',
|
|
||||||
position: 'absolute',
|
|
||||||
top: '8px',
|
|
||||||
right: '8px'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<IconCheck size={12} />
|
<IconCheck size={12} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Метка Fastest */}
|
{/* Метка Fastest */}
|
||||||
<div className="mb-1" style={{ minHeight: '18px' }}>
|
<div className="mb-1 easy-switch-fast-slot">
|
||||||
{isFastest && !isActive && (
|
{isFastest && !isActive && (
|
||||||
<span className="badge bg-yellow-lt text-yellow d-inline-flex align-items-center" style={{ fontSize: '0.65rem', padding: '2px 6px' }}>
|
<span className="badge bg-yellow-lt text-yellow d-inline-flex align-items-center easy-switch-fast-badge">
|
||||||
<IconBolt size={10} className="me-1" />
|
<IconBolt size={10} className="me-1" />
|
||||||
Fast
|
Fast
|
||||||
</span>
|
</span>
|
||||||
@@ -1202,22 +1094,14 @@ function EasySwitchManager() {
|
|||||||
{/* Флаг и название (комментарий) шлюза */}
|
{/* Флаг и название (комментарий) шлюза */}
|
||||||
<div className="d-flex align-items-center gap-2 mb-1">
|
<div className="d-flex align-items-center gap-2 mb-1">
|
||||||
<span
|
<span
|
||||||
className="d-flex align-items-center justify-content-center flex-shrink-0 rounded"
|
className={`d-flex align-items-center justify-content-center flex-shrink-0 rounded easy-switch-gateway-flag ${isActive ? 'easy-switch-gateway-flag-active' : ''}`}
|
||||||
style={{
|
|
||||||
width: '28px',
|
|
||||||
height: '28px',
|
|
||||||
fontSize: '1.25rem',
|
|
||||||
lineHeight: 1,
|
|
||||||
backgroundColor: isActive ? 'rgba(255,255,255,0.25)' : 'rgba(0,0,0,0.06)',
|
|
||||||
}}
|
|
||||||
title={gw.country || country || ''}
|
title={gw.country || country || ''}
|
||||||
>
|
>
|
||||||
{countryToFlag(gw.country || country || '') || '🌐'}
|
{countryToFlag(gw.country || country || '') || '🌐'}
|
||||||
</span>
|
</span>
|
||||||
<div className="flex-grow-1 min-w-0">
|
<div className="flex-grow-1 min-w-0">
|
||||||
<div
|
<div
|
||||||
className={`fw-bold ${isActive ? 'text-white' : ''}`}
|
className={`fw-bold easy-switch-gateway-title ${isActive ? 'text-white' : ''}`}
|
||||||
style={{ fontSize: '0.85rem', lineHeight: '1.2', wordBreak: 'break-word' }}
|
|
||||||
>
|
>
|
||||||
{gw.comment || gw.name}
|
{gw.comment || gw.name}
|
||||||
</div>
|
</div>
|
||||||
@@ -1226,15 +1110,14 @@ function EasySwitchManager() {
|
|||||||
{/* IP шлюза ниже */}
|
{/* IP шлюза ниже */}
|
||||||
{gw.ip && (
|
{gw.ip && (
|
||||||
<div
|
<div
|
||||||
className={`small text-truncate mb-1 ${isActive ? 'text-white-75' : 'text-muted'}`}
|
className={`small text-truncate mb-1 easy-switch-gateway-ip ${isActive ? 'text-white-75' : 'text-muted'}`}
|
||||||
style={{ fontSize: '0.75rem' }}
|
|
||||||
>
|
>
|
||||||
{gw.ip}
|
{gw.ip}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Метрика (пинг) */}
|
{/* Метрика (пинг) */}
|
||||||
<div className={`fw-bold mt-1 ${isActive ? 'text-white' : 'text-success'}`} style={{ fontSize: '1.1rem' }}>
|
<div className={`fw-bold mt-1 easy-switch-gateway-ping ${isActive ? 'text-white' : 'text-success'}`}>
|
||||||
{ping != null ? ping : '—'}
|
{ping != null ? ping : '—'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user