+
+
🏕️ POTA ACTIVATORS
- {loading ? (
-
- ) : data && data.length > 0 ? (
-
- {data.slice(0, 5).map((spot, i) => (
-
-
- {spot.call}
-
-
- {spot.ref}
-
-
- {spot.freq}
-
-
- ))}
-
- ) : (
-
- No active POTA spots
-
- )}
+
+ {loading ? (
+
+ ) : data && data.length > 0 ? (
+
+ {data.slice(0, 5).map((spot, i) => (
+
+
+ {spot.call}
+
+
+ {spot.ref}
+
+
+ {spot.freq}
+
+
+ ))}
+
+ ) : (
+
+ No POTA spots
+
+ )}
+
);
};
diff --git a/src/components/SettingsPanel.jsx b/src/components/SettingsPanel.jsx
index 4069ebc..26baf20 100644
--- a/src/components/SettingsPanel.jsx
+++ b/src/components/SettingsPanel.jsx
@@ -1,55 +1,83 @@
/**
* SettingsPanel Component
- * Modal for app configuration (callsign, location, theme, layout)
+ * Full settings modal matching production version
*/
import React, { useState, useEffect } from 'react';
+import { calculateGridSquare } from '../utils/geo.js';
export const SettingsPanel = ({ isOpen, onClose, config, onSave }) => {
- const [formData, setFormData] = useState({
- callsign: '',
- lat: '',
- lon: '',
- theme: 'dark',
- layout: 'modern'
- });
+ const [callsign, setCallsign] = useState(config?.callsign || '');
+ 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 [dxClusterSource, setDxClusterSource] = useState(config?.dxClusterSource || 'dxspider-proxy');
useEffect(() => {
if (config) {
- setFormData({
- callsign: config.callsign || 'N0CALL',
- lat: config.location?.lat?.toString() || '40.0150',
- lon: config.location?.lon?.toString() || '-105.2705',
- theme: config.theme || 'dark',
- layout: config.layout || 'modern'
- });
+ setCallsign(config.callsign || '');
+ setLat(config.location?.lat || 0);
+ setLon(config.location?.lon || 0);
+ setTheme(config.theme || 'dark');
+ setLayout(config.layout || 'modern');
+ setDxClusterSource(config.dxClusterSource || 'dxspider-proxy');
+ // Calculate grid from coordinates
+ if (config.location?.lat && config.location?.lon) {
+ setGridSquare(calculateGridSquare(config.location.lat, config.location.lon));
+ }
}
}, [config, isOpen]);
- const handleSubmit = (e) => {
- e.preventDefault();
- const newConfig = {
- ...config,
- callsign: formData.callsign.toUpperCase().trim() || 'N0CALL',
- location: {
- lat: parseFloat(formData.lat) || 40.0150,
- lon: parseFloat(formData.lon) || -105.2705
- },
- theme: formData.theme,
- layout: formData.layout
- };
- onSave(newConfig);
- onClose();
+ // Update lat/lon when grid square changes
+ const handleGridChange = (grid) => {
+ setGridSquare(grid.toUpperCase());
+ // Parse grid square to lat/lon if valid (6 char)
+ if (grid.length >= 4) {
+ const parsed = parseGridSquare(grid);
+ if (parsed) {
+ setLat(parsed.lat);
+ setLon(parsed.lon);
+ }
+ }
};
- const handleGeolocate = () => {
+ // Parse grid square to coordinates
+ 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 };
+ };
+
+ // Update grid when lat/lon changes
+ useEffect(() => {
+ if (lat && lon) {
+ setGridSquare(calculateGridSquare(lat, lon));
+ }
+ }, [lat, lon]);
+
+ const handleUseLocation = () => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
(position) => {
- setFormData(prev => ({
- ...prev,
- lat: position.coords.latitude.toFixed(4),
- lon: position.coords.longitude.toFixed(4)
- }));
+ setLat(position.coords.latitude);
+ setLon(position.coords.longitude);
},
(error) => {
console.error('Geolocation error:', error);
@@ -57,193 +85,295 @@ export const SettingsPanel = ({ isOpen, onClose, config, onSave }) => {
}
);
} else {
- alert('Geolocation is not supported by your browser.');
+ alert('Geolocation not supported by your browser.');
}
};
+ const handleSave = () => {
+ onSave({
+ ...config,
+ callsign: callsign.toUpperCase(),
+ location: { lat: parseFloat(lat), lon: parseFloat(lon) },
+ theme,
+ layout,
+ dxClusterSource
+ });
+ onClose();
+ };
+
if (!isOpen) return null;
- const inputStyle = {
- width: '100%',
- padding: '10px 12px',
- background: 'var(--bg-tertiary)',
- border: '1px solid var(--border-color)',
- borderRadius: '6px',
- color: 'var(--text-primary)',
- fontSize: '14px',
- fontFamily: 'JetBrains Mono, monospace'
+ const themeDescriptions = {
+ dark: '→ Modern dark theme (default)',
+ light: '→ Light theme for daytime use',
+ legacy: '→ Classic green terminal style'
};
- const labelStyle = {
- display: 'block',
- fontSize: '12px',
- color: 'var(--text-secondary)',
- marginBottom: '6px',
- fontWeight: '500'
+ const layoutDescriptions = {
+ modern: '→ Modern responsive grid layout',
+ classic: '→ Classic HamClock-style layout'
};
return (
+ }}>
e.stopPropagation()}>
- {/* Header */}
-
+
- ⚙ Settings
-