import { useState, useMemo } from 'react';
import {
IconServer,
IconWorld,
IconBuildingSkyscraper,
IconNetwork,
IconChevronDown,
IconChevronRight,
IconFilter,
IconSearch,
IconDatabase,
IconX
} from '@tabler/icons-react';
function countryToFlag(isoCode) {
if (!isoCode) return '';
const codeMap = {
SWE: 'SE',
RUS: 'RU',
USA: 'US',
GER: 'DE',
FIN: 'FI',
NLD: 'NL'
};
const code = codeMap[isoCode?.toUpperCase()] || isoCode?.toUpperCase();
if (!code || code.length !== 2) return isoCode;
return code.replace(/./g, char => String.fromCodePoint(127397 + char.charCodeAt()));
}
function FilterSection({ title, icon: _Icon, iconColor, expanded, onToggle, children }) {
return (
{expanded &&
{children}
}
);
}
function FilterItem({ label, count, active, onClick, leftAddon }) {
return (
);
}
export default function ServerSidebar({
servers = [],
filters = {},
onFiltersChange,
onSearch,
searchTerm = '',
onReset,
/** В режиме true рендерится только контент без обёртки card (для использования внутри offcanvas) */
embedded = false
}) {
const [expandedSections, setExpandedSections] = useState({
country: true,
provider: true,
type: true,
tunnel: false
});
const stats = useMemo(() => {
const byCountry = {};
const byProvider = {};
const byType = {};
const byTunnel = {};
servers.forEach(srv => {
byCountry[srv.country] = (byCountry[srv.country] || 0) + 1;
byProvider[srv.provider] = (byProvider[srv.provider] || 0) + 1;
byType[srv.type] = (byType[srv.type] || 0) + 1;
byTunnel[srv.tunnel] = (byTunnel[srv.tunnel] || 0) + 1;
});
return { byCountry, byProvider, byType, byTunnel };
}, [servers]);
const toggleSection = section => {
setExpandedSections(prev => ({ ...prev, [section]: !prev[section] }));
};
const handleFilterClick = (category, value) => {
const current = filters[category];
onFiltersChange({ ...filters, [category]: current === value ? '' : value });
};
const activeFiltersCount = Object.values(filters).filter(Boolean).length + (searchTerm ? 1 : 0);
const typeLabels = { jumphost: 'Jumphost', exit: 'Выходная нода' };
const content = (
<>
{!embedded && (
Фильтры
{activeFiltersCount > 0 && (
{activeFiltersCount}
)}
{activeFiltersCount > 0 && (
)}
)}
toggleSection('country')}
>
{Object.entries(stats.byCountry)
.sort((a, b) => b[1] - a[1])
.map(([country, count]) => (
handleFilterClick('country', country)}
leftAddon={
{countryToFlag(country)}
}
/>
))}
toggleSection('provider')}
>
{Object.entries(stats.byProvider)
.sort((a, b) => b[1] - a[1])
.map(([provider, count]) => (
handleFilterClick('provider', provider)}
/>
))}
toggleSection('type')}
>
{Object.entries(stats.byType)
.sort((a, b) => b[1] - a[1])
.map(([type, count]) => (
handleFilterClick('type', type)}
leftAddon={
}
/>
))}
toggleSection('tunnel')}
>
{Object.entries(stats.byTunnel)
.sort((a, b) => b[1] - a[1])
.map(([tunnel, count]) => (
handleFilterClick('tunnel', tunnel)}
/>
))}
Статистика
Всего серверов
{servers.length}
Выходных нод
{stats.byType['exit'] || 0}
Jumphosts
{stats.byType['jumphost'] || 0}
>
);
if (embedded) {
return {content}
;
}
return (
{content}
);
}