import { useMemo } from 'react'; import { Star, Radio } from 'lucide-react'; import { Badge } from '@/components/ui/badge'; import { cn } from '@/lib/utils'; import type { WorkedBeforeView } from '@/types'; type WorkedBefore = WorkedBeforeView; interface Props { wb: WorkedBefore | null; busy: boolean; currentBand: string; currentMode: string; } // 13-column band layout — no 4m, no SHF (per user preference). const BANDS: { tag: string; label: string }[] = [ { tag: '160m', label: '160' }, { tag: '80m', label: '80' }, { tag: '60m', label: '60' }, { tag: '40m', label: '40' }, { tag: '30m', label: '30' }, { tag: '20m', label: '20' }, { tag: '17m', label: '17' }, { tag: '15m', label: '15' }, { tag: '12m', label: '12' }, { tag: '10m', label: '10' }, { tag: '6m', label: '6' }, { tag: '2m', label: 'V' }, { tag: '70cm', label: 'U' }, ]; const CLASSES = ['PH', 'CW', 'DIG'] as const; const PHONE_MODES = new Set(['SSB','USB','LSB','AM','FM','DIGITALVOICE','PHONE']); function classMatchesMode(cls: string, mode: string): boolean { const u = (mode || '').toUpperCase(); if (cls === 'PH') return PHONE_MODES.has(u); if (cls === 'CW') return u === 'CW'; return u !== '' && u !== 'CW' && !PHONE_MODES.has(u); } const STATUS_CLASSES: Record = { call_c: 'bg-emerald-700 hover:bg-emerald-600', call_w: 'bg-emerald-300 hover:bg-emerald-200', dxcc_c: 'bg-indigo-800 hover:bg-indigo-700', dxcc_w: 'bg-indigo-300 hover:bg-indigo-200', }; // Legend entries, in the same colour order as the cells. swatch = the // background class (or a special ring marker for the current-entry cell). const LEGEND: { swatch: string; ring?: boolean; label: string }[] = [ { swatch: 'bg-emerald-700', label: 'Call confirmed' }, { swatch: 'bg-emerald-300', label: 'Call worked' }, { swatch: 'bg-indigo-800', label: 'Entity confirmed' }, { swatch: 'bg-indigo-300', label: 'Entity worked' }, { swatch: 'bg-stone-200', label: 'Not worked' }, { swatch: 'bg-stone-200', ring: true, label: 'Current entry' }, ]; function cellTitle(band: string, cls: string, status: string, current: boolean): string { const desc = status === 'call_c' ? 'This callsign confirmed' : status === 'call_w' ? 'This callsign worked (not confirmed)' : status === 'dxcc_c' ? 'Entity confirmed (other callsign)' : status === 'dxcc_w' ? 'Entity worked (other callsign)' : 'Never worked'; return `${band} ${cls}: ${desc}${current ? ' — current entry' : ''}`; } export function BandSlotGrid({ wb, busy, currentBand, currentMode }: Props) { const dxcc = wb?.dxcc ?? 0; const dxccName = wb?.dxcc_name ?? ''; const dxccCount = wb?.dxcc_count ?? 0; const callCount = wb?.count ?? 0; // QSOs with this exact callsign const hasDxcc = dxcc > 0; const newOne = hasDxcc && dxccCount === 0; const statusMap = useMemo(() => { const m = new Map(); for (const s of wb?.band_status ?? []) { m.set(`${s.band}|${s.class}`, s.status); } return m; }, [wb]); return (
{newOne ? ( <> NEW ONE {dxccName || `DXCC #${dxcc}`} {' '}· never worked this entity ) : hasDxcc ? ( <> {dxccName || `DXCC #${dxcc}`} {dxccCount}{' '} QSO{dxccCount > 1 ? 's' : ''} with this entity {callCount > 0 && ( <> {' · '} {callCount}{' '} with this call )} ) : busy ? ( looking up… ) : ( Type a callsign to see entity stats )}
))} {CLASSES.map((cls) => { const classCurrent = classMatchesMode(cls, currentMode); return ( {BANDS.map((b) => { const st = statusMap.get(`${b.tag}|${cls}`) ?? ''; const isCurrent = b.tag === currentBand && classCurrent; return ( ); })}
{BANDS.map((b) => ( {b.label}
{cls} ); })}
{/* Colour legend — sits in the spare room under the matrix. */}
{LEGEND.map((l) => ( {l.label} ))}
); }