This commit is contained in:
2026-06-13 01:34:45 +02:00
parent 408b29896c
commit 3cb2e466d8
21 changed files with 1285 additions and 130 deletions
+30 -5
View File
@@ -17,6 +17,7 @@ import {
GetClublogCtyInfo, SetClublogCtyEnabled, DownloadClublogCty,
GetSecretStatus, SetPassphrase, RemovePassphrase,
GetEmailSettings, SaveEmailSettings, TestEmail,
QSLGetEmailTemplates, QSLSaveEmailTemplates,
GetDVKMessages, SetDVKLabel, DVKStartRecord, DVKStopRecord, DVKPreview, DVKStop, GetDVKStatus,
ListClusterServers, SaveClusterServer, DeleteClusterServer,
GetClusterAutoConnect, SetClusterAutoConnect,
@@ -462,6 +463,10 @@ export function SettingsModal({ onClose, onSaved, initialSection }: Props) {
});
const [emailMsg, setEmailMsg] = useState('');
const setEmailField = (patch: Partial<EmailCfg>) => setEmailCfg((s) => ({ ...s, ...patch }));
// eQSL card e-mail (subject/body templates + auto-send on log).
type EQSLCfg = { subject: string; body: string; auto_send: boolean };
const [eqslCfg, setEqslCfg] = useState<EQSLCfg>({ subject: '', body: '', auto_send: false });
const setEqslField = (patch: Partial<EQSLCfg>) => setEqslCfg((s) => ({ ...s, ...patch }));
// ClubLog Country File (cty.xml) exception status.
type ClubInfo = { enabled: boolean; loaded: boolean; date: string; count: number };
const [clubInfo, setClubInfo] = useState<ClubInfo>({ enabled: false, loaded: false, date: '', count: 0 });
@@ -632,6 +637,7 @@ export function SettingsModal({ onClose, onSaved, initialSection }: Props) {
try { setWkPorts((await ListSerialPorts() ?? []) as string[]); } catch {}
try { setAudioCfg(await GetAudioSettings() as any); } catch {}
try { setEmailCfg(await GetEmailSettings() as any); } catch {}
try { setEqslCfg(await QSLGetEmailTemplates() as any); } catch {}
reloadAudioDevices();
reloadDvk();
} catch (e: any) {
@@ -791,6 +797,7 @@ export function SettingsModal({ onClose, onSaved, initialSection }: Props) {
await SaveWinkeyerSettings(wk as any);
await SaveAudioSettings(audioCfg as any);
await SaveEmailSettings(emailCfg as any);
await QSLSaveEmailTemplates(eqslCfg as any);
await SaveBackupSettings(backupCfg as any);
await SaveQSLDefaults(qslDefaults as any);
await SaveExternalServices(extSvc as any);
@@ -860,16 +867,16 @@ export function SettingsModal({ onClose, onSaved, initialSection }: Props) {
<Input className="font-mono uppercase" value={p.operator ?? ''} onChange={(e) => updateActive({ operator: e.target.value })} placeholder="F4XYZ" />
<div className="text-[10px] text-muted-foreground">Who's at the radio (ADIF OPERATOR).</div>
</div>
<div className="space-y-1 col-span-2">
<Label>Operator name</Label>
<Input className="max-w-xs" value={p.op_name ?? ''} onChange={(e) => updateActive({ op_name: e.target.value })} placeholder="e.g. Greg" />
<div className="text-[10px] text-muted-foreground">Your first name used as the signature on QSL cards.</div>
</div>
<div className="space-y-1 col-span-2">
<Label>Owner callsign</Label>
<Input className="font-mono uppercase max-w-xs" value={p.owner_callsign ?? ''} onChange={(e) => updateActive({ owner_callsign: e.target.value })} placeholder="(leave blank if same as station)" />
<div className="text-[10px] text-muted-foreground">Legal station owner only differs at club stations or remote setups (ADIF STATION_OWNER).</div>
</div>
<div className="space-y-1 col-span-2">
<Label>Operator name</Label>
<Input className="max-w-xs" value={p.op_name ?? ''} onChange={(e) => updateActive({ op_name: e.target.value })} placeholder="e.g. Greg" />
<div className="text-[10px] text-muted-foreground">Your first name used as the signature on QSL cards.</div>
</div>
<div className="col-span-2 text-[10px] text-muted-foreground uppercase tracking-wider mt-1">
Auto-filled from the callsign editable (stamped as MY_* on each QSO)
</div>
@@ -3077,6 +3084,24 @@ export function SettingsModal({ onClose, onSaved, initialSection }: Props) {
</Button>
<span className="text-[11px] text-muted-foreground">{emailMsg}</span>
</div>
<div className="pt-2 mt-2 border-t border-border space-y-2">
<Label className="text-sm font-semibold">eQSL card e-mail</Label>
<div className="text-[11px] text-muted-foreground">
Message sent with the QSL card. Variables: {'{CALL}'} {'{DATE}'} {'{BAND}'} {'{MODE}'} {'{MYCALL}'}.
</div>
<Input className="h-8" placeholder="Subject" value={eqslCfg.subject}
onChange={(e) => setEqslField({ subject: e.target.value })} />
<Textarea rows={3} className="text-sm" placeholder="Body" value={eqslCfg.body}
onChange={(e) => setEqslField({ body: e.target.value })} />
<label className="flex items-center gap-2 text-sm cursor-pointer">
<Checkbox checked={eqslCfg.auto_send} onCheckedChange={(c) => setEqslField({ auto_send: !!c })} />
Auto-send eQSL when a QSO is logged
</label>
<div className="text-[11px] text-muted-foreground">
Sends automatically only when the contact has an e-mail address and a default QSL template exists.
</div>
</div>
</div>
</>
);