up
This commit is contained in:
@@ -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 & 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>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user