import { useMemo, useState } from 'react'; import { Construction } from 'lucide-react'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Checkbox } from '@/components/ui/checkbox'; import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem, } from '@/components/ui/select'; import { cn } from '@/lib/utils'; import { pathBetween } from '@/lib/maidenhead'; import { BandSlotGrid } from '@/components/BandSlotGrid'; export interface DetailsState { state: string; cnty: string; address: string; lat?: number; lon?: number; // DXCC entity number + zones (filled from QRZ/HamQTH or cty.dat fallback). // Editable so the operator can correct an obviously wrong auto-fill. dxcc?: number; cqz?: number; ituz?: number; cont: string; qsl_msg: string; qsl_via: string; ant_az?: number; ant_el?: number; ant_path: string; prop_mode: string; my_rig: string; my_antenna: string; tx_pwr?: number; sat_name: string; sat_mode: string; contest_id: string; srx?: number; stx?: number; email: string; } interface Props { callsign: string; prefix: string; operatorGrid: string; // station.my_grid — origin for bearing/distance remoteGrid: string; // entry-strip Grid value — destination details: DetailsState; onChange: (patch: Partial) => void; // Stats (F1) tab content: the worked-before matrix + optional QRZ image. wb?: any; wbBusy?: boolean; band: string; mode: string; imageUrl?: string; onOpenImage?: () => void; // Optional controlled active tab (so the app can switch it via keyboard). tab?: TabName; onTab?: (t: TabName) => void; // When the WinKeyer is active, F1-F12 fire macros, so the tab shortcut is // shown as Ctrl+F1…F5 instead of F1…F5. keyerActive?: boolean; } export type TabName = 'stats' | 'info' | 'awards' | 'my' | 'extended'; const PROP_MODES = ['NONE','AS','AUE','AUR','BS','ECH','EME','ES','F2','F2M','FAI','GWAVE','INTERNET','ION','IRL','LOS','MS','RPT','RS','SAT','TEP','TR']; function numOrUndef(v: string): number | undefined { if (v === '') return undefined; const n = parseFloat(v); return isNaN(n) ? undefined : n; } // Compact field helper to keep the JSX dense. function Field({ label, span = 1, children }: { label: string; span?: 1 | 2 | 3 | 6; children: React.ReactNode }) { return (
{children}
); } export function DetailsPanel({ callsign: _cs, prefix, operatorGrid, remoteGrid, details, onChange, wb, wbBusy, band, mode, tab, onTab, keyerActive }: Props) { const [internalOpen, setInternalOpen] = useState('stats'); const open = tab ?? internalOpen; // controlled when `tab` is provided // Bearing/distance from operator's home grid to the remote station. // Recomputed only when either grid actually changes. const path = useMemo( () => pathBetween(operatorGrid, remoteGrid), [operatorGrid, remoteGrid], ); const fmtDeg = (n: number) => `${Math.round(n)}°`; const fmtKm = (n: number) => `${Math.round(n).toLocaleString()} km`; function toggle(t: TabName) { onTab ? onTab(t) : setInternalOpen(t); } const fk = keyerActive ? 'Ctrl+F' : 'F'; const satelliteMode = !!details.sat_name || !!details.sat_mode || details.prop_mode === 'SAT'; function setSatellite(on: boolean) { if (on) { if (details.prop_mode !== 'SAT') onChange({ prop_mode: 'SAT' }); } else { onChange({ sat_name: '', sat_mode: '', ...(details.prop_mode === 'SAT' ? { prop_mode: '' } : {}), }); } } const tabs: { key: TabName; label: string }[] = [ { key: 'stats', label: `Stats (${fk}1)` }, { key: 'info', label: `Info (${fk}2)` }, { key: 'awards', label: `Awards (${fk}3)` }, { key: 'my', label: `My (${fk}4)` }, { key: 'extended', label: `Extended (${fk}5)` }, ]; return (
{open === 'stats' && (
)} {open === 'info' && (
onChange({ state: e.target.value })} /> onChange({ cnty: e.target.value })} /> {/* DXCC #, CQ zone, ITU zone, Continent and Azimuth SP live in the main entry strip — visible without opening F2. F2 keeps the less-needed long-path bearing and both distances. */} onChange({ address: e.target.value })} /> onChange({ qsl_msg: e.target.value })} /> onChange({ qsl_via: e.target.value })} />
)} {open === 'awards' && (
Awards module coming soon
)} {open === 'my' && (
onChange({ ant_az: numOrUndef(e.target.value) })} /> onChange({ ant_el: numOrUndef(e.target.value) })} /> onChange({ ant_path: e.target.value })} /> onChange({ tx_pwr: numOrUndef(e.target.value) })} />
onChange({ my_rig: e.target.value })} /> onChange({ my_antenna: e.target.value })} /> {satelliteMode && ( <> onChange({ sat_name: e.target.value })} /> onChange({ sat_mode: e.target.value })} /> )}
)} {open === 'extended' && (
onChange({ contest_id: e.target.value })} /> onChange({ srx: numOrUndef(e.target.value) })} /> onChange({ stx: numOrUndef(e.target.value) })} /> onChange({ email: e.target.value })} />
)}
); }