/**
* DXFilterManager Component
* Filter modal with tabs for Zones, Bands, Modes, Watchlist, Exclude, Settings
*/
import React, { useState } from 'react';
export const DXFilterManager = ({ filters, onFilterChange, isOpen, onClose }) => {
const [activeTab, setActiveTab] = useState('zones');
const [newWatchlistCall, setNewWatchlistCall] = useState('');
const [newExcludeCall, setNewExcludeCall] = useState('');
if (!isOpen) return null;
const continents = [
{ code: 'NA', name: 'North America' },
{ code: 'SA', name: 'South America' },
{ code: 'EU', name: 'Europe' },
{ code: 'AF', name: 'Africa' },
{ code: 'AS', name: 'Asia' },
{ code: 'OC', name: 'Oceania' },
{ code: 'AN', name: 'Antarctica' }
];
const bands = ['160m', '80m', '60m', '40m', '30m', '20m', '17m', '15m', '12m', '11m', '10m', '6m', '2m', '70cm'];
const modes = ['CW', 'SSB', 'FT8', 'FT4', 'RTTY', 'PSK', 'JT65', 'JS8', 'SSTV', 'AM', 'FM'];
const toggleArrayItem = (key, item) => {
const current = filters[key] || [];
const newArray = current.includes(item)
? current.filter(x => x !== item)
: [...current, item];
onFilterChange({ ...filters, [key]: newArray.length ? newArray : undefined });
};
const selectAll = (key, items) => {
onFilterChange({ ...filters, [key]: [...items] });
};
const clearFilter = (key) => {
const newFilters = { ...filters };
delete newFilters[key];
onFilterChange(newFilters);
};
const clearAllFilters = () => {
onFilterChange({});
};
const getActiveFilterCount = () => {
let count = 0;
if (filters?.continents?.length) count += filters.continents.length;
if (filters?.cqZones?.length) count += filters.cqZones.length;
if (filters?.ituZones?.length) count += filters.ituZones.length;
if (filters?.bands?.length) count += filters.bands.length;
if (filters?.modes?.length) count += filters.modes.length;
if (filters?.watchlist?.length) count += filters.watchlist.length;
if (filters?.excludeList?.length) count += filters.excludeList.length;
return count;
};
const tabStyle = (active) => ({
padding: '8px 16px',
background: active ? 'var(--bg-tertiary)' : 'transparent',
border: 'none',
borderBottom: active ? '2px solid var(--accent-cyan)' : '2px solid transparent',
color: active ? 'var(--accent-cyan)' : 'var(--text-muted)',
fontSize: '13px',
cursor: 'pointer',
fontFamily: 'inherit'
});
const chipStyle = (selected) => ({
padding: '6px 12px',
background: selected ? 'rgba(0, 221, 255, 0.2)' : 'var(--bg-tertiary)',
border: `1px solid ${selected ? 'var(--accent-cyan)' : 'var(--border-color)'}`,
borderRadius: '4px',
color: selected ? 'var(--accent-cyan)' : 'var(--text-secondary)',
fontSize: '12px',
cursor: 'pointer',
fontFamily: 'JetBrains Mono, monospace'
});
const zoneButtonStyle = (selected) => ({
width: '36px',
height: '32px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: selected ? 'rgba(0, 221, 255, 0.2)' : 'var(--bg-tertiary)',
border: `1px solid ${selected ? 'var(--accent-cyan)' : 'var(--border-color)'}`,
borderRadius: '4px',
color: selected ? 'var(--accent-cyan)' : 'var(--text-secondary)',
fontSize: '12px',
cursor: 'pointer',
fontFamily: 'JetBrains Mono, monospace'
});
const addToWatchlist = () => {
if (newWatchlistCall.trim()) {
const current = filters?.watchlist || [];
if (!current.includes(newWatchlistCall.toUpperCase())) {
onFilterChange({ ...filters, watchlist: [...current, newWatchlistCall.toUpperCase()] });
}
setNewWatchlistCall('');
}
};
const addToExclude = () => {
if (newExcludeCall.trim()) {
const current = filters?.excludeList || [];
if (!current.includes(newExcludeCall.toUpperCase())) {
onFilterChange({ ...filters, excludeList: [...current, newExcludeCall.toUpperCase()] });
}
setNewExcludeCall('');
}
};
const renderZonesTab = () => (
{/* Continents */}
Continents
{continents.map(c => (
))}
{/* CQ Zones */}
CQ Zones
{Array.from({ length: 40 }, (_, i) => i + 1).map(zone => (
))}
{/* ITU Zones */}
ITU Zones
{Array.from({ length: 90 }, (_, i) => i + 1).map(zone => (
))}
);
const renderBandsTab = () => (
HF/VHF/UHF Bands
{bands.map(band => (
))}
);
const renderModesTab = () => (
Operating Modes
{modes.map(mode => (
))}
);
const renderWatchlistTab = () => (
{(filters?.watchlist || []).map(call => (
{call}
))}
);
const renderExcludeTab = () => (
{(filters?.excludeList || []).map(call => (
{call}
))}
);
const renderSettingsTab = () => {
const retentionMinutes = filters?.spotRetentionMinutes || 30;
return (
Spot Retention Time
How long to keep DX spots on the map before they expire. Shorter times show only the most recent activity.
5 min (freshest)
30 min (default)
Quick Presets
{[5, 10, 15, 20, 30].map(mins => (
))}
);
};
return (
{/* Header */}
⊘ DX Cluster Filters
{getActiveFilterCount()} filters active
{/* Tabs */}
{/* Tab Content */}
{activeTab === 'zones' && renderZonesTab()}
{activeTab === 'bands' && renderBandsTab()}
{activeTab === 'modes' && renderModesTab()}
{activeTab === 'watchlist' && renderWatchlistTab()}
{activeTab === 'exclude' && renderExcludeTab()}
{activeTab === 'settings' && renderSettingsTab()}
);
};
export default DXFilterManager;