fix: bulk edit refused the fields it offered (#3)

My mistake yesterday: the new confirmation columns went into uploadStatusCols —
the QSL Manager's FILTER whitelist — instead of bulkEditableCols. The dialog
listed QRZ.com received status and the ten dates, and the repository refused
them at Apply, after the operator had selected 54 QSOs.

Moved to the right map. And two fields have been broken this way since long
before: State and County were offered under "Contacted station" (whose IOTA /
POTA / SOTA / SIG siblings are all allowed) but were missing from the whitelist,
so choosing one always failed. Added — they are exactly what an import loses and
what a whole run shares.

The real fix is the test. Two lists in two languages had to agree by hand and
nothing checked it, so the failure only ever surfaced as an error message at the
last click. The new tests read the ACTUAL field ids out of BulkEditModal.tsx and
FilterBuilder.tsx and assert the repository accepts every one — a field added to
the UI alone now fails the build instead of the operator.
This commit is contained in:
2026-07-28 09:45:22 +02:00
parent d49126d45c
commit 843dccf0c4
9 changed files with 197 additions and 37 deletions
+16 -1
View File
@@ -1052,7 +1052,7 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
const [bandDraft, setBandDraft] = useState('');
const [modeDraft, setModeDraft] = useState('');
const [catCfg, setCatCfg] = useState<CATSettings>({
enabled: false, backend: 'omnirig', omnirig_rig: 1, flex_host: '', flex_port: 4992, flex_spots: false, flex_decode_spots: false, flex_decode_secs: 120,
enabled: false, backend: 'omnirig', omnirig_rig: 1, omnirig_vfo: '', flex_host: '', flex_port: 4992, flex_spots: false, flex_decode_spots: false, flex_decode_secs: 120,
icom_port: '', icom_baud: 115200, icom_addr: 0x98, icom_net_host: '', icom_net_user: '', icom_net_pass: '', icom_net_audio: false,
tci_host: '', tci_port: 40001, tci_spots: false, poll_ms: 250, delay_ms: 0,
digital_default: 'FT8',
@@ -2334,6 +2334,21 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
</Select>
</div>
)}
{catCfg.backend === 'omnirig' && (
<div className="space-y-1">
<Label>{t('cat.omnirigVfo')}</Label>
<Select value={catCfg.omnirig_vfo || 'auto'}
onValueChange={(v) => setCatCfg((s) => ({ ...s, omnirig_vfo: v === 'auto' ? '' : v }))}>
<SelectTrigger><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value="auto">{t('cat.omnirigVfoAuto')}</SelectItem>
<SelectItem value="A">{t('cat.omnirigVfoA')}</SelectItem>
<SelectItem value="B">{t('cat.omnirigVfoB')}</SelectItem>
</SelectContent>
</Select>
<p className="text-[10px] text-muted-foreground">{t('cat.omnirigVfoHint')}</p>
</div>
)}
{catCfg.backend === 'flex' && (
<>
<div className="space-y-1">
+2 -2
View File
@@ -282,7 +282,7 @@ const en: Dict = {
'cat.icomNetHint': "Connects to the rig's built-in LAN server directly — no RS-BA1 or Remote Utility needed (close them first). Use the Network User1 ID/Password set in the rig's Network menu. A rig in standby is powered on automatically.",
'cat.icomNetAudio': 'Stream RX audio over the network (experimental)',
'cat.icomNetAudioHint': 'Play the rigs received audio through your Listening device (Settings → Audio) over the 50003 stream. Experimental — the audio framing is pending on-rig verification; leave off if control misbehaves.',
'cat.omnirigRig': 'OmniRig rig slot', 'cat.flexIp': 'FlexRadio IP', 'cat.port': 'Port', 'cat.flexSpots': 'Show cluster spots on the panadapter', 'cat.flexSpotsHint': "(spots from OpsLog's DX cluster appear on the radio, auto-expire after 30 min)", 'cat.flexDecodeSpots': 'Show WSJT-X decodes on the panadapter', 'cat.flexDecodeSpotsHint': '(heard FT8/FT4 stations from your WSJT-X/JTDX UDP feed, one spot per call)', 'cat.flexDecodeSecs': 'Display for', 'cat.flexDecodeSecsHint': 'seconds before a station is removed',
'cat.omnirigRig': 'OmniRig rig slot', 'cat.omnirigVfo': 'VFO to read', 'cat.omnirigVfoAuto': 'As reported by the rig file', 'cat.omnirigVfoA': 'Always VFO A (main)', 'cat.omnirigVfoB': 'Always VFO B (sub)', 'cat.omnirigVfoHint': 'OmniRig reports the active VFO from the rig file, and some files get it wrong — the frequency then follows the other VFO and appears frozen. Force one here if that happens.', 'cat.flexIp': 'FlexRadio IP', 'cat.port': 'Port', 'cat.flexSpots': 'Show cluster spots on the panadapter', 'cat.flexSpotsHint': "(spots from OpsLog's DX cluster appear on the radio, auto-expire after 30 min)", 'cat.flexDecodeSpots': 'Show WSJT-X decodes on the panadapter', 'cat.flexDecodeSpotsHint': '(heard FT8/FT4 stations from your WSJT-X/JTDX UDP feed, one spot per call)', 'cat.flexDecodeSecs': 'Display for', 'cat.flexDecodeSecsHint': 'seconds before a station is removed',
'cat.icomPort': 'Icom CI-V port', 'cat.selectCom': 'Select COM port', 'cat.noPorts': 'No ports found', 'cat.baud': 'Baud rate', 'cat.icomModel': 'Rig model', 'cat.icomModelOther': 'Other (custom address)', 'cat.civAddr': 'CI-V address (hex)', 'cat.civHint': 'Pick your model to set the CI-V address automatically (or choose "Other" and type it). Set "CI-V USB Echo Back" OFF and CI-V baud to match on the rig.',
'cat.tciHost': 'TCI host', 'cat.tciHint': 'Enable the TCI server in ExpertSDR2/EESDR (Options → TCI). Default port 40001. Use 127.0.0.1 when OpsLog runs on the same PC.', 'cat.tciSpots': 'Show cluster spots on the panorama', 'cat.tciSpotsHint': "(spots from OpsLog's DX cluster appear on the SDR panadapter)",
'cat.pollMs': 'Poll interval (ms)', 'cat.delayMs': 'CAT delay (ms)', 'cat.digitalDefault': 'Default digital mode (when rig reports DIG)', 'cat.modeBeforeFreq': 'Set mode before frequency', 'cat.modeBeforeFreqHint': '(older rigs that drop the mode after a band change)',
@@ -684,7 +684,7 @@ const fr: Dict = {
'cat.icomNetHint': "Se connecte directement au serveur LAN intégré du poste — sans RS-BA1 ni Remote Utility (ferme-les d'abord). Utilise l'ID/mot de passe Network User1 configurés dans le menu Network du poste. Un poste en veille est allumé automatiquement.",
'cat.icomNetAudio': 'Diffuser laudio RX par le réseau (expérimental)',
'cat.icomNetAudioHint': 'Écoute laudio reçu du poste sur ton périphérique d’écoute (Réglages → Audio) via le flux 50003. Expérimental — le format audio reste à vérifier sur le poste ; laisse désactivé si le contrôle se comporte mal.',
'cat.omnirigRig': 'Slot OmniRig', 'cat.flexIp': 'IP FlexRadio', 'cat.port': 'Port', 'cat.flexSpots': 'Afficher les spots cluster sur le panadapter', 'cat.flexSpotsHint': "(les spots du cluster DX d'OpsLog apparaissent sur la radio, expirent après 30 min)", 'cat.flexDecodeSpots': 'Afficher les décodes WSJT-X sur le panadapter', 'cat.flexDecodeSpotsHint': '(stations FT8/FT4 entendues via ton flux UDP WSJT-X/JTDX, un spot par station)', 'cat.flexDecodeSecs': 'Affichage pendant', 'cat.flexDecodeSecsHint': 'secondes avant retrait d\'une station',
'cat.omnirigRig': 'Slot OmniRig', 'cat.omnirigVfo': 'VFO à lire', 'cat.omnirigVfoAuto': 'Selon le fichier radio', 'cat.omnirigVfoA': 'Toujours le VFO A (principal)', 'cat.omnirigVfoB': 'Toujours le VFO B (secondaire)', 'cat.omnirigVfoHint': "OmniRig indique le VFO actif d'après le fichier radio, et certains fichiers se trompent — la fréquence suit alors l'autre VFO et paraît figée. Forcez-en un ici le cas échéant.", 'cat.flexIp': 'IP FlexRadio', 'cat.port': 'Port', 'cat.flexSpots': 'Afficher les spots cluster sur le panadapter', 'cat.flexSpotsHint': "(les spots du cluster DX d'OpsLog apparaissent sur la radio, expirent après 30 min)", 'cat.flexDecodeSpots': 'Afficher les décodes WSJT-X sur le panadapter', 'cat.flexDecodeSpotsHint': '(stations FT8/FT4 entendues via ton flux UDP WSJT-X/JTDX, un spot par station)', 'cat.flexDecodeSecs': 'Affichage pendant', 'cat.flexDecodeSecsHint': 'secondes avant retrait d\'une station',
'cat.icomPort': 'Port CI-V Icom', 'cat.selectCom': 'Choisir un port COM', 'cat.noPorts': 'Aucun port trouvé', 'cat.baud': 'Débit (baud)', 'cat.icomModel': 'Modèle de poste', 'cat.icomModelOther': 'Autre (adresse perso)', 'cat.civAddr': 'Adresse CI-V (hex)', 'cat.civHint': 'Choisis ton modèle pour fixer ladresse CI-V automatiquement (ou « Autre » et saisis-la). Mets « CI-V USB Echo Back » sur OFF et fais correspondre le débit CI-V sur le poste.',
'cat.tciHost': 'Hôte TCI', 'cat.tciHint': 'Active le serveur TCI dans ExpertSDR2/EESDR (Options → TCI). Port par défaut 40001. Utilise 127.0.0.1 si OpsLog tourne sur le même PC.', 'cat.tciSpots': 'Afficher les spots cluster sur le panorama', 'cat.tciSpotsHint': "(les spots du cluster DX d'OpsLog apparaissent sur le panadapter SDR)",
'cat.pollMs': 'Intervalle de poll (ms)', 'cat.delayMs': 'Délai CAT (ms)', 'cat.digitalDefault': 'Mode numérique par défaut (quand le poste indique DIG)', 'cat.modeBeforeFreq': 'Régler le mode avant la fréquence', 'cat.modeBeforeFreqHint': '(anciens postes qui perdent le mode après un changement de bande)',