Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7204c4045f | ||
|
|
ab8b30fbd4 |
@@ -36,6 +36,7 @@ function EasySwitchManager() {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [selectedRule, setSelectedRule] = useState('all'); // all | by-community
|
||||
const [activeTab, setActiveTab] = useState('proxies'); // proxies | providers
|
||||
const [showOnlyConfigured, setShowOnlyConfigured] = useState(true); // Показывать только настроенные
|
||||
|
||||
// Активные шлюзы для каждого community (ключ: community, значение: gateway name)
|
||||
const [activeGateways, setActiveGateways] = useState({});
|
||||
@@ -331,21 +332,22 @@ function EasySwitchManager() {
|
||||
|
||||
{/* Панель фильтров */}
|
||||
<div className="mb-3">
|
||||
<div className="row g-2">
|
||||
{/* Фильтр по правилу */}
|
||||
<div className="col-md-3">
|
||||
<select
|
||||
className="form-select"
|
||||
value={selectedRule}
|
||||
onChange={(e) => setSelectedRule(e.target.value)}
|
||||
>
|
||||
<option value="all">Правило</option>
|
||||
<option value="by-community">По community</option>
|
||||
</select>
|
||||
<div className="row g-2 align-items-center">
|
||||
{/* Переключатель "Только настроенные" */}
|
||||
<div className="col-auto">
|
||||
<label className="form-check form-switch mb-0">
|
||||
<input
|
||||
className="form-check-input"
|
||||
type="checkbox"
|
||||
checked={showOnlyConfigured}
|
||||
onChange={(e) => setShowOnlyConfigured(e.target.checked)}
|
||||
/>
|
||||
<span className="form-check-label">Только настроенные фильтры</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{/* Поиск */}
|
||||
<div className="col-md-6">
|
||||
<div className="col">
|
||||
<div className="input-icon">
|
||||
<span className="input-icon-addon">
|
||||
<IconSearch size={18} />
|
||||
@@ -370,7 +372,7 @@ function EasySwitchManager() {
|
||||
</div>
|
||||
|
||||
{/* Инструменты */}
|
||||
<div className="col-auto ms-auto">
|
||||
<div className="col-auto">
|
||||
<button className="btn btn-icon" title="Настройки">
|
||||
<IconSettings size={18} />
|
||||
</button>
|
||||
@@ -401,6 +403,17 @@ function EasySwitchManager() {
|
||||
const country = serverMeta?.country || '';
|
||||
const flag = countryToFlag(country);
|
||||
|
||||
// Фильтруем communities для этого сервера
|
||||
const filteredCommunitiesForServer = communitiesDirectory.filter((comm) => {
|
||||
if (showOnlyConfigured) {
|
||||
return server.filtersMap.has(comm.value);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// Не показываем сервер, если нет отфильтрованных communities
|
||||
if (filteredCommunitiesForServer.length === 0) return null;
|
||||
|
||||
return (
|
||||
<div key={server.id} className="card mb-3">
|
||||
{/* Заголовок сервера */}
|
||||
@@ -413,14 +426,14 @@ function EasySwitchManager() {
|
||||
<span className="text-muted small">— {server.description}</span>
|
||||
)}
|
||||
<span className="badge bg-blue-lt text-blue ms-auto">
|
||||
{communitiesDirectory.length} communities • {server.gateways.length} gateways
|
||||
{filteredCommunitiesForServer.length} communities • {server.gateways.length} gateways
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="card-body p-3">
|
||||
{/* ВСЕ Communities для этого сервера */}
|
||||
{communitiesDirectory.map((comm) => {
|
||||
{filteredCommunitiesForServer.map((comm) => {
|
||||
const activeKey = `${server.id}:${comm.value}`;
|
||||
const activeGw = activeGateways[activeKey];
|
||||
const hasFilterForComm = server.filtersMap.has(comm.value);
|
||||
@@ -444,28 +457,28 @@ function EasySwitchManager() {
|
||||
</div>
|
||||
|
||||
{/* Сетка карточек ВСЕх шлюзов сервера */}
|
||||
<div className="row g-2">
|
||||
<div className="row g-3">
|
||||
{server.gateways.map((gw, idx) => {
|
||||
const isActive = activeGw === gw.name;
|
||||
const isFastest = idx === 0;
|
||||
const ping = 20 + idx * 10 + Math.floor(Math.random() * 15);
|
||||
|
||||
return (
|
||||
<div key={gw.name} className="col-6 col-sm-4 col-md-3 col-lg-2">
|
||||
<div key={gw.name} className="col-12 col-sm-6 col-md-4">
|
||||
<div
|
||||
className={`card cursor-pointer h-100 ${isActive ? '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'
|
||||
borderRadius: '8px',
|
||||
boxShadow: isActive ? '0 4px 12px rgba(32, 107, 196, 0.3)' : 'none'
|
||||
}}
|
||||
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)';
|
||||
e.currentTarget.style.boxShadow = '0 2px 8px rgba(0,0,0,0.1)';
|
||||
e.currentTarget.style.transform = 'translateY(-2px)';
|
||||
}
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
@@ -475,12 +488,12 @@ function EasySwitchManager() {
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="card-body p-2">
|
||||
<div className="card-body p-3">
|
||||
{/* Галочка и метка Fastest */}
|
||||
<div className="d-flex align-items-start justify-content-between mb-1">
|
||||
<div className="d-flex align-items-start justify-content-between mb-2">
|
||||
{isFastest && !isActive && (
|
||||
<span className="badge bg-yellow-lt text-yellow d-inline-flex align-items-center" style={{ fontSize: '0.65rem', padding: '2px 4px' }}>
|
||||
<IconBolt size={10} className="me-1" />
|
||||
<span className="badge bg-yellow-lt text-yellow d-inline-flex align-items-center" style={{ fontSize: '0.75rem', padding: '4px 8px' }}>
|
||||
<IconBolt size={14} className="me-1" />
|
||||
Fast
|
||||
</span>
|
||||
)}
|
||||
@@ -488,25 +501,25 @@ function EasySwitchManager() {
|
||||
{isActive && (
|
||||
<div
|
||||
className="bg-white text-primary rounded-circle d-flex align-items-center justify-content-center"
|
||||
style={{ width: '18px', height: '18px' }}
|
||||
style={{ width: '24px', height: '24px' }}
|
||||
>
|
||||
<IconCheck size={12} />
|
||||
<IconCheck size={16} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Название шлюза */}
|
||||
<div className={`fw-bold mb-1 ${isActive ? 'text-white' : ''}`} style={{ fontSize: '0.8rem' }}>
|
||||
<div className={`fw-bold mb-2 ${isActive ? 'text-white' : ''}`} style={{ fontSize: '1rem' }}>
|
||||
{gw.name}
|
||||
</div>
|
||||
|
||||
{/* Протокол */}
|
||||
<div className={`small ${isActive ? 'text-white' : 'text-muted'}`} style={{ fontSize: '0.7rem', opacity: isActive ? 0.85 : 1 }}>
|
||||
{serverMeta?.provider || 'vless'}
|
||||
<div className={`small mb-2 ${isActive ? 'text-white' : 'text-muted'}`} style={{ fontSize: '0.85rem', opacity: isActive ? 0.85 : 1 }}>
|
||||
{serverMeta?.provider || 'vless / xudp'}
|
||||
</div>
|
||||
|
||||
{/* Метрика (пинг) */}
|
||||
<div className={`fw-bold mt-1 ${isActive ? 'text-white' : 'text-success'}`} style={{ fontSize: '1rem' }}>
|
||||
<div className={`fw-bold ${isActive ? 'text-white' : 'text-success'}`} style={{ fontSize: '1.5rem' }}>
|
||||
{ping}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user