feat(hamlog): show what the confirmation import did, and export what it could not place

The import printed four numbers and left nothing behind. 1106 matched
and 395 unmatched out of 1501 is not a report: the operator cannot see
which contacts were confirmed, nor work through the discrepancies.

Two additions. The Results view is now filled with the confirmed
contacts, flagged new entity/band/mode/slot against what LoTW and paper
QSL already confirm -- the only sense in which a HAMLOG confirmation
changes anything. And the unmatched ones can be written out as ADIF,
because a few hundred of them are a list to open and compare, not
something to read in a log window.
This commit is contained in:
2026-08-23 15:43:57 +02:00
parent b952e074f4
commit ab89611eca
7 changed files with 128 additions and 6 deletions
+22 -1
View File
@@ -8,7 +8,7 @@ import {
Select, SelectTrigger, SelectValue, SelectContent, SelectItem,
} from '@/components/ui/select';
import { cn } from '@/lib/utils';
import { FindQSOsForUpload, UploadQSOsManual, DownloadConfirmations, CancelConfirmations, ImportHamlogConfirmations, OpenADIFFile, SyncPOTAHunterLog, ListQSO, BulkUpdateQSL, UploadCallsign, GetSlotStats } from '../../wailsjs/go/main/App';
import { FindQSOsForUpload, UploadQSOsManual, DownloadConfirmations, CancelConfirmations, ImportHamlogConfirmations, ExportHamlogUnmatched, OpenADIFFile, SaveADIFFile, SyncPOTAHunterLog, ListQSO, BulkUpdateQSL, UploadCallsign, GetSlotStats } from '../../wailsjs/go/main/App';
import { Input } from '@/components/ui/input';
import { RecentQSOsGrid } from '@/components/RecentQSOsGrid';
import { EventsOn } from '../../wailsjs/runtime/runtime';
@@ -363,6 +363,21 @@ export function QSLManagerPanel({ onEditQSO, actions, paperRequest }: {
catch (e: any) { setLogLines((p) => [...p, 'Error: ' + String(e?.message ?? e)]); setBusy(false); }
}
// The unmatched half of an import. Offered as a file because a few hundred
// discrepancies are a list to work through, not something to read in a log
// window.
async function exportHamlogUnmatched() {
try {
const path = await SaveADIFFile();
if (!path) return;
const n = await ExportHamlogUnmatched(path);
setLogLines((p) => [...p, `Exported ${n} unmatched confirmation(s) → ${path}`]);
setShowLog(true);
} catch (e: any) {
setLogLines((p) => [...p, 'Error: ' + String(e?.message ?? e)]); setShowLog(true);
}
}
function viewResults() {
setShowLog(false);
if (logAction === 'upload') selectRequired();
@@ -680,10 +695,16 @@ export function QSLManagerPanel({ onEditQSO, actions, paperRequest }: {
<div className="flex items-center justify-between gap-2 px-3 py-2 border-t border-border bg-muted/20 shrink-0">
<div className="flex items-center gap-2 flex-wrap">
{service === 'hamlog' ? (
<>
<Button variant="outline" size="sm" onClick={importHamlogCfm} disabled={busy}
title={t('qslm.hamlogImportTitle')}>
<DownloadCloud className="size-3.5" /> {t('qslm.hamlogImportCfm')}
</Button>
<Button variant="outline" size="sm" onClick={exportHamlogUnmatched} disabled={busy}
title={t('qslm.hamlogUnmatchedTitle')}>
<UploadCloud className="size-3.5 rotate-180" /> {t('qslm.hamlogUnmatched')}
</Button>
</>
) : (
<Button variant="outline" size="sm" onClick={download} disabled={busy}
title={t('qslm.downloadTitle')}>
File diff suppressed because one or more lines are too long
+2
View File
@@ -233,6 +233,8 @@ export function ExportCabrilloFiltered(arg1:string,arg2:qso.QueryFilter):Promise
export function ExportCabrilloSelected(arg1:string,arg2:Array<number>):Promise<main.CabrilloResult>;
export function ExportHamlogUnmatched(arg1:string):Promise<number>;
export function FilterFields():Promise<Array<string>>;
export function FindDuplicates(arg1:number):Promise<Array<main.DuplicateGroup>>;
+4
View File
@@ -406,6 +406,10 @@ export function ExportCabrilloSelected(arg1, arg2) {
return window['go']['main']['App']['ExportCabrilloSelected'](arg1, arg2);
}
export function ExportHamlogUnmatched(arg1) {
return window['go']['main']['App']['ExportHamlogUnmatched'](arg1);
}
export function FilterFields() {
return window['go']['main']['App']['FilterFields']();
}