feat: day batch — upload results surfaced as toasts (silent LoTW failures from the right-click send looked like nothing was sent); live selection count + Select all/Unselect all toggle in the grid toolbar; active filters bypass the row limit (all matches shown, 10k safety cap); NET Control: same right-click menu on worked-before + drag&drop roster<->on-air (drop on roster logs the QSO); compact mode restores previous window size/position; advanced-filter field renamed Station callsign; optional DXCC-style digital-mode grouping (Settings->General) for matrix badges + cluster colouring; changelog 0.20.9 entries (version NOT bumped)

This commit is contained in:
2026-07-22 15:38:08 +02:00
parent 5aac28f564
commit 4c75680689
10 changed files with 284 additions and 39 deletions
+13 -6
View File
@@ -109,14 +109,21 @@ export function BandSlotGrid({ wb, busy, currentBand, currentMode, bands, hasCal
// "Newness" of the current band+mode entry, for the award/DX-chase badges.
// Derived straight from the entity's real band_status (all bands it was
// worked on — not just the operator's configured column list).
// Newness uses the ACTUAL mode (FT8 / FT4 / RTTY…), not the PH/CW/DIG class:
// DIG is a group, so FT4 after FT8 is genuinely a new mode. dxcc_band_modes
// lists every real (band, mode) the entity was worked on.
// By default newness uses the ACTUAL mode (FT8 / FT4 / RTTY…): DIG is a
// group, so FT4 after FT8 is genuinely a new mode. The operator can opt into
// DXCC-style grouping instead (Settings → General), where all digital modes
// count as ONE — then FT4 after FT8 is just "worked".
const groupDigital = localStorage.getItem('opslog.groupDigitalSlots') === '1';
const normMode = (m: string): string => {
const u = (m || '').toUpperCase().trim();
if (!groupDigital) return u;
return u === '' || u === 'CW' || PHONE_MODES.has(u) ? u : 'DIG';
};
const bandModes = (wb?.dxcc_band_modes ?? []) as { band: string; mode: string }[];
const curMode = (currentMode || '').toUpperCase().trim();
const curMode = normMode(currentMode);
const bandWorked = bandModes.some((bm) => bm.band === currentBand); // entity worked on this band (any mode)
const modeWorked = !!curMode && bandModes.some((bm) => (bm.mode || '').toUpperCase() === curMode); // …in this exact mode (any band)
const slotWorked = !!curMode && bandModes.some((bm) => bm.band === currentBand && (bm.mode || '').toUpperCase() === curMode);
const modeWorked = !!curMode && bandModes.some((bm) => normMode(bm.mode) === curMode); // …in this (normalised) mode (any band)
const slotWorked = !!curMode && bandModes.some((bm) => bm.band === currentBand && normMode(bm.mode) === curMode);
// Mutually-exclusive badges, shown only when the entity is worked but this
// exact band+mode is NOT yet:
// New Band & Mode = both the band AND the mode are new for this entity.