/**
* SettingsPanel Component
* Full settings modal with map layer controls
*/
import React, { useState, useEffect } from 'react';
import { calculateGridSquare } from '../utils/geo.js';
import { useTranslation, Trans } from 'react-i18next';
import { LANGUAGES } from '../lang/i18n.js';
export const SettingsPanel = ({ isOpen, onClose, config, onSave }) => {
const [callsign, setCallsign] = useState(config?.callsign || '');
const [callsignSize, setCallsignSize] = useState(config?.callsignSize || 1.0);
const [gridSquare, setGridSquare] = useState('');
const [lat, setLat] = useState(config?.location?.lat || 0);
const [lon, setLon] = useState(config?.location?.lon || 0);
const [theme, setTheme] = useState(config?.theme || 'dark');
const [layout, setLayout] = useState(config?.layout || 'modern');
const [timezone, setTimezone] = useState(config?.timezone || '');
const [dxClusterSource, setDxClusterSource] = useState(config?.dxClusterSource || 'dxspider-proxy');
const { t, i18n } = useTranslation();
// Layer controls
const [layers, setLayers] = useState([]);
const [activeTab, setActiveTab] = useState('station');
useEffect(() => {
if (config) {
setCallsign(config.callsign || '');
setCallsignSize(config.callsignSize || 1.0)
setLat(config.location?.lat || 0);
setLon(config.location?.lon || 0);
setTheme(config.theme || 'dark');
setLayout(config.layout || 'modern');
setTimezone(config.timezone || '');
setDxClusterSource(config.dxClusterSource || 'dxspider-proxy');
if (config.location?.lat && config.location?.lon) {
setGridSquare(calculateGridSquare(config.location.lat, config.location.lon));
}
}
}, [config, isOpen]);
// Load layers when panel opens
useEffect(() => {
if (isOpen && window.hamclockLayerControls) {
setLayers(window.hamclockLayerControls.layers || []);
}
}, [isOpen]);
// Refresh layers periodically
useEffect(() => {
if (isOpen && activeTab === 'layers') {
const interval = setInterval(() => {
if (window.hamclockLayerControls) {
setLayers([...window.hamclockLayerControls.layers]);
}
}, 200);
return () => clearInterval(interval);
}
}, [isOpen, activeTab]);
const handleGridChange = (grid) => {
setGridSquare(grid.toUpperCase());
if (grid.length >= 4) {
const parsed = parseGridSquare(grid);
if (parsed) {
setLat(parsed.lat);
setLon(parsed.lon);
}
}
};
const parseGridSquare = (grid) => {
grid = grid.toUpperCase();
if (grid.length < 4) return null;
const lon1 = (grid.charCodeAt(0) - 65) * 20 - 180;
const lat1 = (grid.charCodeAt(1) - 65) * 10 - 90;
const lon2 = parseInt(grid[2]) * 2;
const lat2 = parseInt(grid[3]) * 1;
let lon = lon1 + lon2 + 1;
let lat = lat1 + lat2 + 0.5;
if (grid.length >= 6) {
const lon3 = (grid.charCodeAt(4) - 65) * (2/24);
const lat3 = (grid.charCodeAt(5) - 65) * (1/24);
lon = lon1 + lon2 + lon3 + (1/24);
lat = lat1 + lat2 + lat3 + (1/48);
}
return { lat, lon };
};
useEffect(() => {
if (lat && lon) {
setGridSquare(calculateGridSquare(lat, lon));
}
}, [lat, lon]);
const handleUseLocation = () => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
(position) => {
setLat(position.coords.latitude);
setLon(position.coords.longitude);
},
(error) => {
console.error('Geolocation error:', error);
alert(t('station.settings.useLocation.error1'));
}
);
} else {
alert(t('station.settings.useLocation.error2'));
}
};
const handleToggleLayer = (layerId) => {
if (window.hamclockLayerControls) {
const layer = layers.find(l => l.id === layerId);
const newEnabledState = !layer.enabled;
// Update the control
window.hamclockLayerControls.toggleLayer(layerId, newEnabledState);
// Force immediate UI update
setLayers(prevLayers =>
prevLayers.map(l =>
l.id === layerId ? { ...l, enabled: newEnabledState } : l
)
);
// Refresh after a short delay to get the updated state
setTimeout(() => {
if (window.hamclockLayerControls) {
setLayers([...window.hamclockLayerControls.layers]);
}
}, 100);
}
};
const handleOpacityChange = (layerId, opacity) => {
if (window.hamclockLayerControls) {
window.hamclockLayerControls.setOpacity(layerId, opacity);
setLayers([...window.hamclockLayerControls.layers]);
}
};
const handleSave = () => {
onSave({
...config,
callsign: callsign.toUpperCase(),
callsignSize: callsignSize,
location: { lat: parseFloat(lat), lon: parseFloat(lon) },
theme,
layout,
timezone,
dxClusterSource
});
onClose();
};
if (!isOpen) return null;
const Code = ({ children }) => (
{children}
);
const themeDescriptions = {
dark: t('station.settings.theme.dark.describe'),
light: t('station.settings.theme.light.describe'),
legacy: t('station.settings.theme.legacy.describe'),
retro: t('station.settings.theme.retro.describe')
};
const layoutDescriptions = {
modern: t('station.settings.layout.modern.describe'),
classic: t('station.settings.layout.classic.describe')
};
return (
}} />