diff --git a/changelog.json b/changelog.json index e4ce43c..9881f64 100644 --- a/changelog.json +++ b/changelog.json @@ -7,14 +7,16 @@ "HamQTH: an “Upload the whole log” button in the QSL Manager — one file instead of one request per QSO, so a first sync takes seconds rather than the better part of an hour. It REPLACES the log held on HamQTH (the site has no partial upload), so it asks first, is scoped to the callsign this profile uploads as, and compresses a large log to stay under the 20 MB limit.", "Outbound ADIF (forwarding a logged QSO to another logger such as Log4OM): the receive side is filled in when the contact was not split, so BAND_RX and FREQ_RX are present. The importer already did this; the logging paths did not, so what a QSO carried depended on which door it came in through.", "HamQTH joins the places the other services already were: two Recent-QSOs columns (sent status and date), the QSO filter, and bulk edit — the last one so a log uploaded to HamQTH by hand can be marked as sent instead of being offered for upload all over again.", - "HamQTH whole-log upload: it reports itself in the console like every other action — the callsign it is scoped to, how many of the logbook’s QSOs that leaves, the file size, and HamQTH’s own reply — and says plainly that HamQTH imports the file in the background and e-mails any ADIF errors, so a site count that lags or stops short is explained rather than mysterious." + "HamQTH whole-log upload: it reports itself in the console like every other action — the callsign it is scoped to, how many of the logbook’s QSOs that leaves, the file size, and HamQTH’s own reply — and says plainly that HamQTH imports the file in the background and e-mails any ADIF errors, so a site count that lags or stops short is explained rather than mysterious.", + "QSO editor: correcting a frequency now moves its band with it, TX and RX — a QSO fixed to 7.1 MHz no longer stays filed on 20m. The band is only touched when the frequency lands in a known allocation, so a half-typed number never blanks it." ], "fr": [ "DX Cluster : un serveur déconnecté garde sa pastille et peut donc être reconnecté — le déconnecter le faisait disparaître avec le seul moyen d’y revenir.", "HamQTH : un bouton « Envoyer tout le log » dans le QSL Manager — un seul fichier au lieu d’une requête par QSO, une première synchro passe de près d’une heure à quelques secondes. Il REMPLACE le log stocké sur HamQTH (le site n’a pas d’envoi partiel) : il demande donc confirmation, se limite à l’indicatif du profil et compresse un gros log pour rester sous la limite de 20 Mo.", "ADIF sortant (transfert d’un QSO vers un autre log, Log4OM par exemple) : le côté réception est renseigné quand le contact n’était pas en split, donc BAND_RX et FREQ_RX sont présents. L’import le faisait déjà, pas les chemins de log — ce qu’un QSO transportait dépendait donc de la porte par laquelle il était entré.", "HamQTH rejoint les endroits où les autres services étaient déjà : deux colonnes dans les QSO récents (statut et date d’envoi), le filtre de QSO et l’édition groupée — cette dernière pour qu’un log envoyé à la main sur HamQTH puisse être marqué comme envoyé au lieu d’être reproposé à l’envoi.", - "Envoi du log complet HamQTH : il rend compte dans la console comme toutes les autres actions — l’indicatif retenu, combien de QSO du journal cela représente, la taille du fichier et la réponse de HamQTH — et indique clairement que HamQTH importe le fichier en arrière-plan et envoie les erreurs ADIF par e-mail : un compteur en retard ou incomplet sur le site est ainsi expliqué au lieu d’être mystérieux." + "Envoi du log complet HamQTH : il rend compte dans la console comme toutes les autres actions — l’indicatif retenu, combien de QSO du journal cela représente, la taille du fichier et la réponse de HamQTH — et indique clairement que HamQTH importe le fichier en arrière-plan et envoie les erreurs ADIF par e-mail : un compteur en retard ou incomplet sur le site est ainsi expliqué au lieu d’être mystérieux.", + "Éditeur de QSO : corriger une fréquence déplace désormais sa bande avec elle, TX comme RX — un QSO corrigé à 7,1 MHz ne reste plus classé en 20m. La bande n’est touchée que si la fréquence tombe dans une allocation connue : un nombre à moitié tapé ne l’efface jamais." ] }, { diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 3b369da..7f32289 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -59,6 +59,7 @@ import { Combobox } from '@/components/ui/combobox'; import { applyAwardRefs, parseAwardRefs as parseManualRefs, spotRefList , withIOTARef, withRDARef } from '@/lib/awardRefs'; import { ListRadios, SetActiveRadio } from '../wailsjs/go/main/App'; import { formatDistance } from '@/lib/units'; +import { bandForMHz } from '@/lib/bandplan'; import { EventsOn, BrowserOpenURL, WindowMinimise, WindowToggleMaximise, WindowIsMaximised, Quit } from '../wailsjs/runtime/runtime'; import type { adif as adifModels, lookup as lookupModels, cat as catModels } from '../wailsjs/go/models'; import type { QSOForm, WorkedBeforeView, StationSettingsForm, ListsSettingsForm, ModePresetForm } from '@/types'; @@ -432,21 +433,6 @@ function entryQSYCommand(text: string): { band?: string; hz?: number } | null { return null; } -function bandForMHz(mhz: number): string { - if (!mhz || isNaN(mhz)) return ''; - const plan: [number, number, string][] = [ - [1.8, 2.0, '160m'], [3.5, 4.0, '80m'], [5.06, 5.45, '60m'], [7.0, 7.3, '40m'], - [10.1, 10.15, '30m'], [14.0, 14.35, '20m'], [18.068, 18.168, '17m'], [21.0, 21.45, '15m'], - [24.89, 24.99, '12m'], [28.0, 29.7, '10m'], [50, 54, '6m'], [70, 71, '4m'], - [144, 148, '2m'], [222, 225, '1.25m'], [420, 450, '70cm'], [902, 928, '33cm'], [1240, 1300, '23cm'], - // Microwave, ADIF 3.1.7 ranges — kept in step with BandFromHz on the Go side. - [2300, 2450, '13cm'], [3300, 3500, '9cm'], [5650, 5925, '6cm'], [10000, 10500, '3cm'], - [24000, 24250, '1.25cm'], [47000, 47200, '6mm'], [75500, 81000, '4mm'], - [119980, 123000, '2.5mm'], [134000, 149000, '2mm'], [241000, 250000, '1mm'], - ]; - for (const [lo, hi, b] of plan) if (mhz >= lo && mhz <= hi) return b; - return ''; -} // modeAccent maps a mode to a theme-aware colour for the live-stations widget: // CW gold, phone green, digital blue, unknown muted. diff --git a/frontend/src/components/QSOEditModal.tsx b/frontend/src/components/QSOEditModal.tsx index b211ebe..7dffd10 100644 --- a/frontend/src/components/QSOEditModal.tsx +++ b/frontend/src/components/QSOEditModal.tsx @@ -22,6 +22,7 @@ import { Combobox } from '@/components/ui/combobox'; import { cn } from '@/lib/utils'; import { flagURL } from '@/lib/flags'; import { useI18n } from '@/lib/i18n'; +import { bandForMHz } from '@/lib/bandplan'; import { titleCase, sentenceCase } from '@/lib/textCase'; import type { QSOForm } from '@/types'; @@ -312,6 +313,19 @@ export function QSOEditModal({ qso, onSave, onDelete, onClose, countries = [], b const splitHz = (hz?: number) => hz ? { khz: String(Math.floor(hz / 1000)), hz: String(hz % 1000).padStart(3, '0') } : { khz: '', hz: '' }; + // Correcting a frequency corrects its band. The pair has to agree — the log, + // every award and every upload are read on the BAND — and an operator fixing + // a wrong frequency is not also expecting to fix the band by hand, which is + // exactly how a QSO ends up filed on 20m at 7 MHz. + // + // Only when the number lands in a known allocation: half a frequency is typed + // on the way to all of it, and a band must never be blanked by that. + const syncBand = (khz: string, hz: string, field: 'band' | 'band_rx') => { + if (!khz.trim()) return; + const b = bandForMHz((parseInt(khz, 10) * 1000 + (parseInt(hz, 10) || 0)) / 1_000_000); + if (b) set(field, b as any); + }; + const f0 = splitHz(draft.freq_hz); const fr0 = splitHz(draft.freq_rx_hz); const [freqKHz, setFreqKHz] = useState(f0.khz); @@ -634,13 +648,13 @@ export function QSOEditModal({ qso, onSave, onDelete, onClose, countries = [], b