This commit is contained in:
2026-06-14 01:35:40 +02:00
parent 67203cd4a8
commit 29fd832bcd
7 changed files with 290 additions and 58 deletions
+32 -12
View File
@@ -543,6 +543,7 @@ export function SettingsModal({ onClose, onSaved, initialSection }: Props) {
const [mysqlCfg, setMysqlCfg] = useState<MySQLCfg>({ enabled: false, host: '', port: 3306, user: '', password: '', database: '' });
const setMysqlField = (patch: Partial<MySQLCfg>) => setMysqlCfg((s) => ({ ...s, ...patch }));
const [mysqlMsg, setMysqlMsg] = useState('');
const [restartMsg, setRestartMsg] = useState(''); // backend switch / save → "restart to apply"
const [backendStatus, setBackendStatus] = useState<{ active: string; fallback: boolean; error: string } | null>(null);
const [dataDir, setDataDir] = useState('');
@@ -2184,7 +2185,9 @@ export function SettingsModal({ onClose, onSaved, initialSection }: Props) {
<>
<SectionHeader
title="Database backup"
hint="OpsLog can copy the SQLite database to a folder of your choice when you close it, once per day. Rotation keeps the last N copies and deletes older ones."
hint={mysqlCfg.enabled
? "On close (once/day) OpsLog snapshots the local SQLite (config) AND exports the shared MySQL log to ADIF — opslog-log-<date>.adi — so your contacts are protected even though they live on the server. Rotation keeps the last N of each."
: "OpsLog can copy the SQLite database to a folder of your choice when you close it, once per day. Rotation keeps the last N copies and deletes older ones."}
/>
<div className="space-y-4 max-w-xl">
<label className="flex items-center gap-2 text-sm cursor-pointer">
@@ -2697,10 +2700,24 @@ export function SettingsModal({ onClose, onSaved, initialSection }: Props) {
<SectionHeader
title="Database"
/>
{/* Backend selector — top of the panel. SQLite (solo) vs MySQL (shared). */}
<div className="grid grid-cols-[130px_1fr] gap-2 items-center max-w-2xl mb-5">
{/* Backend selector — top of the panel. SQLite (solo) vs MySQL (shared).
The choice is persisted immediately (it lives in config.json, read
before the DB opens) so switching to SQLite isn't lost when the MySQL
panel below which holds its own Save button disappears. */}
<div className="grid grid-cols-[130px_1fr] gap-2 items-center max-w-2xl mb-3">
<Label className="text-sm">Backend</Label>
<Select value={mysqlCfg.enabled ? 'mysql' : 'sqlite'} onValueChange={(v) => setMysqlField({ enabled: v === 'mysql' })}>
<Select
value={mysqlCfg.enabled ? 'mysql' : 'sqlite'}
onValueChange={(v) => {
const next = { ...mysqlCfg, enabled: v === 'mysql' };
setMysqlCfg(next);
SaveMySQLSettings(next as any)
.then(() => setRestartMsg(next.enabled
? 'MySQL selected — fill in the connection below, Test, then restart.'
: 'Switched to local SQLite — restart OpsLog to apply.'))
.catch((e: any) => setErr(String(e?.message ?? e)));
}}
>
<SelectTrigger className="h-8 w-72"><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value="sqlite">SQLite local file (solo)</SelectItem>
@@ -2709,6 +2726,15 @@ export function SettingsModal({ onClose, onSaved, initialSection }: Props) {
</Select>
</div>
{/* Restart prompt shown after any backend change (works in both states,
unlike the MySQL panel's own Save which is hidden when SQLite). */}
{restartMsg && (
<div className="max-w-2xl mb-4 text-xs bg-emerald-50 border border-emerald-300 text-emerald-800 rounded-md px-3 py-2 flex items-center justify-between gap-3">
<span>{restartMsg}</span>
<Button size="sm" variant="destructive" className="shrink-0" onClick={() => QuitApp()}>Quit now</Button>
</div>
)}
{/* Active-backend status: confirms what OpsLog actually opened at launch. */}
{backendStatus && (
<div className="max-w-2xl mb-4">
@@ -2767,7 +2793,7 @@ export function SettingsModal({ onClose, onSaved, initialSection }: Props) {
{mysqlCfg.enabled && (
<div className="space-y-3 max-w-2xl">
<div className="text-[11px] text-muted-foreground leading-relaxed">
Several OpsLog instances pointed at one MySQL server see each other's QSOs live (à la Log4OM). Test the connection, then <strong>Save</strong> — OpsLog switches to MySQL (and creates all tables) on the next launch.
Several OpsLog instances pointed at one MySQL server see each other's QSOs live. Test the connection, then <strong>Save</strong> OpsLog switches to MySQL (and creates all tables) on the next launch.
</div>
<div className="grid grid-cols-[130px_1fr] gap-2 items-center">
<Label className="text-sm">Host</Label>
@@ -2787,17 +2813,11 @@ export function SettingsModal({ onClose, onSaved, initialSection }: Props) {
Test &amp; create database
</Button>
<Button size="sm" className="h-8"
onClick={() => { SaveMySQLSettings(mysqlCfg as any).then(() => setMysqlMsg('Saved restart OpsLog to connect to MySQL.')).catch((e: any) => setErr(String(e?.message ?? e))); }}>
onClick={() => { SaveMySQLSettings(mysqlCfg as any).then(() => setRestartMsg('Saved — restart OpsLog to connect to MySQL.')).catch((e: any) => setErr(String(e?.message ?? e))); }}>
Save
</Button>
<span className="text-[11px] text-muted-foreground">{mysqlMsg}</span>
</div>
{mysqlMsg.startsWith('Saved') && (
<div className="text-xs bg-emerald-50 border border-emerald-300 text-emerald-800 rounded-md px-3 py-2 flex items-center justify-between gap-3">
<span>Saved. OpsLog will use the shared MySQL database after a restart.</span>
<Button size="sm" variant="destructive" className="shrink-0" onClick={() => QuitApp()}>Quit now</Button>
</div>
)}
</div>
)}