From 37e9e288babff578d5ab6edfb96f0bcedeba6976 Mon Sep 17 00:00:00 2001 From: rouggy Date: Wed, 26 Aug 2026 11:57:02 +0200 Subject: [PATCH] fix(awards): band columns follow the contacts, not the permission MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The columns took the award's declared band list when it had one, so every band the award PERMITS got a column whether or not anything had ever been worked on it. DDFM permits 6m and 70cm; a station that has never worked a French department on either was shown two empty columns, which reads as a gap in the log rather than as a band nobody tried. They now follow the bands this operator actually has contacts on for the award. The declared list is the fallback for an award with nothing worked yet — there, saying what the award is played on is the only honest answer available — and the classic HF set is the fallback for an award that declares nothing either. Together with taking the order from the full band list, this is what puts 23cm on DDFM: the column appears because there are contacts on it. --- changelog.json | 4 ++-- frontend/src/components/AwardsPanel.tsx | 27 ++++++++++++++++--------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/changelog.json b/changelog.json index 776067f..660697e 100644 --- a/changelog.json +++ b/changelog.json @@ -6,13 +6,13 @@ "Awards, RDA district comparison: each disagreement can now be settled on its own row. Click the district you keep — the log's or the database's — and apply. It is written into the contact's CNTY and its award reference, and the settled rows leave the list.", "The band/mode matrix: a confirmed slot now shows as confirmed. A callsign worked and not confirmed was outranking an entity CONFIRMED on the same band and mode, so a slot that needs nothing was painted as if it still did.", "CAT sharing: a program connected to OpsLog's rigctl server is told the frequency it is LISTENING on. With split engaged it was given the transmit VFO instead, so a client that reads the dial and writes it back — WSJT-X and its like — could move VFO A when VFO B was turned.", - "Awards: the band matrix goes past 70cm. Its columns were a fixed list that stopped there, so a band an award covers — 23cm on DDFM, and every microwave band above it — had no column and no way to get one." + "Awards: the band matrix shows the bands you have contacts on — no more, and no longer capped at 70cm. It used to list every band the award permits, so DDFM stood there with empty 6m and 70cm columns, and it could not show 23cm at all whatever the award said." ], "fr": [ "Diplômes, comparaison des districts RDA : chaque divergence se règle désormais sur sa propre ligne. Cliquer le district qu'on garde — celui du log ou celui de la base — puis appliquer. Il est écrit dans le CNTY du contact et dans sa référence de diplôme, et les lignes réglées quittent la liste.", "Matrice bandes/modes : une case confirmée s'affiche enfin comme confirmée. Un indicatif travaillé et non confirmé l'emportait sur une entité CONFIRMÉE sur la même bande et le même mode, si bien qu'une case qui ne demandait plus rien était peinte comme s'il manquait encore quelque chose.", "Partage CAT : un programme connecté au serveur rigctl d'OpsLog reçoit la fréquence sur laquelle on ÉCOUTE. En split, c'était le VFO d'émission qui lui était donné, si bien qu'un client qui lit le VFO et le réécrit — WSJT-X et consorts — pouvait déplacer le VFO A quand on tournait le VFO B.", - "Diplômes : la matrice des bandes va au-delà du 70cm. Ses colonnes étaient une liste figée qui s'arrêtait là, si bien qu'une bande couverte par un diplôme — le 23cm en DDFM, et toutes les bandes hyper au-dessus — n'avait aucune colonne ni moyen d'en obtenir une." + "Diplômes : la matrice des bandes affiche les bandes sur lesquelles on a des contacts — pas davantage, et sans s'arrêter au 70cm. Elle listait toutes les bandes autorisées par le diplôme, si bien que le DDFM restait avec des colonnes 6m et 70cm vides, et ne pouvait pas afficher le 23cm quoi que dise le diplôme." ] }, { diff --git a/frontend/src/components/AwardsPanel.tsx b/frontend/src/components/AwardsPanel.tsx index 734d0e8..6b399e3 100644 --- a/frontend/src/components/AwardsPanel.tsx +++ b/frontend/src/components/AwardsPanel.tsx @@ -196,18 +196,25 @@ export function AwardsPanel({ onEditQSO, onAwardsChanged, onPaperQSL }: { useEffect(() => { if (selected) compute(selected); /* eslint-disable-next-line react-hooks/exhaustive-deps */ }, [modeFilter]); // Bands relevant to the selected award, used by BOTH the grid and the stats - // matrix so neither is padded with bands the award doesn't use. Rule: the - // award's explicit valid_bands if it has any (e.g. WAPC = 40/20/15/10m); else - // the bands the operator actually has contacts on (so DXCC, which has no band - // restriction, shows 160m–6m instead of empty VHF/UHF columns). Empty set = - // no basis yet → callers fall back to all bands. + // matrix so neither is padded with bands nothing was worked on. Rule: the + // bands this operator HAS contacts on for this award; failing that, the + // award's own list of permitted bands. Empty set = no basis at all → callers + // fall back to the classic columns. const awardBands = useMemo(() => { - const vb = (awardList.find((a) => a.code === selected)?.bands ?? []).map((b) => b.toLowerCase()); - if (vb.length > 0) return new Set(vb); - const worked = (current?.bands ?? []) + // The bands with CONTACTS on them come first, whatever the award declares. + // + // It used to take the declared list when there was one, which put up a + // column for every band the award permits — DDFM allows 6m and 70cm, so + // both stood there empty on a station that has never worked a French + // department on either. An always-blank column reads as a gap in the log + // rather than as a band the operator never tried. + const worked = new Set((current?.bands ?? []) .filter((b) => (b.worked ?? 0) > 0) - .map((b) => String(b.band).toLowerCase()); - return new Set(worked); + .map((b) => String(b.band).toLowerCase())); + if (worked.size > 0) return worked; + // Nothing worked yet: the award's own list is the honest answer — it says + // what this award is played on, which is the only thing there is to show. + return new Set((awardList.find((a) => a.code === selected)?.bands ?? []).map((b) => b.toLowerCase())); }, [awardList, selected, current]); const gridBands = useMemo(() => {