This commit is contained in:
2026-06-06 11:59:32 +02:00
parent 176cc0e62b
commit f91f9ff3b8
13 changed files with 866 additions and 90 deletions
+28 -9
View File
@@ -22,9 +22,10 @@ import {
ConnectAllClusters, DisconnectAllClusters, GetClusterStatus,
GetBackupSettings, SaveBackupSettings, RunBackupNow, PickBackupFolder,
GetDatabaseSettings, PickOpenDatabase, PickSaveDatabase, OpenDatabase, MoveDatabase, ResetDatabaseToDefault, QuitApp, CreateDatabase,
GetDataDir,
GetQSLDefaults, SaveQSLDefaults,
GetExternalServices, SaveExternalServices, TestQRZUpload, TestClublogUpload,
GetPOTAToken, SavePOTAToken, SyncPOTAHunterLog,
GetPOTAToken, SavePOTAToken,
TestLoTWUpload, ListTQSLStationLocations,
ComputeStationInfo,
} from '../../wailsjs/go/main/App';
@@ -486,7 +487,7 @@ export function SettingsModal({ onClose, onSaved, initialSection }: Props) {
// POTA hunter-log sync (stamps pota_ref on local QSOs from your pota.app log).
const [potaToken, setPotaToken] = useState('');
const [potaBusy, setPotaBusy] = useState(false);
const [potaResult, setPotaResult] = useState<{ ok: boolean; msg: string } | null>(null);
const [potaResult, setPotaResult] = useState<{ ok: boolean; msg: string; unmatched?: any[] } | null>(null);
useEffect(() => { GetPOTAToken().then((t) => setPotaToken(t || '')).catch(() => {}); }, []);
const [backupCfg, setBackupCfg] = useState<mainModels.BackupSettings>({
@@ -498,6 +499,7 @@ export function SettingsModal({ onClose, onSaved, initialSection }: Props) {
const [dbSettings, setDbSettings] = useState<{ path: string; default_path: string; is_custom: boolean }>({ path: '', default_path: '', is_custom: false });
const [dbMsg, setDbMsg] = useState('');
const [dataDir, setDataDir] = useState('');
const [clusterServers, setClusterServers] = useState<ClusterServer[]>([]);
const [clusterAutoConnect, setClusterAutoConnectState] = useState(false);
@@ -585,6 +587,7 @@ export function SettingsModal({ onClose, onSaved, initialSection }: Props) {
setQslDefaults(qd as any);
setExtSvc(es as any);
try { setDbSettings(await GetDatabaseSettings() as any); } catch {}
try { setDataDir(await GetDataDir()); } catch {}
try {
const locs: any = await ListTQSLStationLocations();
setStationLocations((locs ?? []).map((l: any) => l.name).filter(Boolean));
@@ -2132,13 +2135,12 @@ export function SettingsModal({ onClose, onSaved, initialSection }: Props) {
{ k: 'pota', label: 'POTA', ready: true },
];
async function syncPota() {
async function savePotaToken() {
setPotaBusy(true);
setPotaResult(null);
try {
await SavePOTAToken(potaToken);
const r: any = await SyncPOTAHunterLog();
setPotaResult({ ok: true, msg: `${r.updated} QSO updated · ${r.already_tagged} already tagged · ${r.unmatched} unmatched (of ${r.fetched} hunter-log entries).` });
setPotaResult({ ok: true, msg: 'Token saved. Run the sync from the QSL Manager → POTA hunter log.' });
} catch (e: any) {
setPotaResult({ ok: false, msg: String(e?.message ?? e) });
} finally {
@@ -2476,10 +2478,12 @@ export function SettingsModal({ onClose, onSaved, initialSection }: Props) {
/>
</div>
<div className="flex items-center gap-3">
<Button onClick={syncPota} disabled={potaBusy || !potaToken.trim()}>
{potaBusy ? <><Loader2 className="size-4 mr-1.5 animate-spin" /> Syncing…</> : 'Sync hunter log'}
<Button onClick={savePotaToken} disabled={potaBusy}>
{potaBusy ? <><Loader2 className="size-4 mr-1.5 animate-spin" /> Saving…</> : 'Save token'}
</Button>
<span className="text-[11px] text-muted-foreground">Matches by callsign + band within ±5 min. Only fills QSOs without a POTA ref.</span>
<span className="text-[11px] text-muted-foreground">
Then run the sync from the <strong>QSL Manager</strong> tab → service <strong>POTA hunter log</strong> (you can see and fix unmatched QSOs there).
</span>
</div>
{potaResult && (
<div className={cn('text-xs rounded-md px-3 py-2 border',
@@ -2487,7 +2491,6 @@ export function SettingsModal({ onClose, onSaved, initialSection }: Props) {
{potaResult.msg}
</div>
)}
<p className="text-[11px] text-muted-foreground">After a sync, rescan the POTA award to see the new references counted.</p>
</div>
) : (
<div className="flex flex-col items-center justify-center text-muted-foreground gap-2 py-16">
@@ -2578,6 +2581,22 @@ export function SettingsModal({ onClose, onSaved, initialSection }: Props) {
)}
</div>
{/* Data location */}
<div className="border-t border-border/60 mt-6 pt-5 space-y-3 max-w-2xl">
<div>
<div className="text-sm font-medium">Data location</div>
<div className="text-[11px] text-muted-foreground mt-0.5">
OpsLog is fully portable — all data lives next to the executable so you can run it from a USB stick or reinstall Windows without losing anything.
</div>
</div>
<div className="space-y-1">
<Label>Current data directory</Label>
<div className="font-mono text-xs bg-muted/40 border border-border rounded-md px-3 py-2 break-all">
{dataDir || '—'}
</div>
</div>
</div>
{/* Backup settings, merged into this Database section. */}
<div className="border-t border-border/60 mt-6 pt-5">
{BackupPanel()}