Initial codebase: Go + Wails amateur radio logbook
Backend (Go 1.25 / Wails v2): - QSO storage on SQLite (modernc) with embedded migrations (0001..0005) - Streaming ADIF import (batch insert) + WorkedBefore per callsign and DXCC - Callsign lookup with QRZ.com + HamQTH providers (primary/failsafe routing) and SQLite-backed TTL cache - DXCC resolver from cty.dat (auto-download, longest-prefix-match) - Multi-profile operator identities (home/portable/SOTA/contest) — every QSO stamps MY_* from the active profile - CAT control via OmniRig COM on a single OS-locked goroutine, with bidirectional sync (freq/mode/band/split/VFOs) and Rig1/Rig2 hot-swap - Settings store (key/value), CAT debug log at %APPDATA%/HamLog/cat.log Frontend (React 18 + TypeScript + Tailwind v4 + shadcn-style): - Single-row entry strip with CAT-aware band/mode/freq, RST, Start/End UTC, per-field locks (band/mode/freq/start/end) for backdated QSOs - Topbar: live freq (MHz.kHz.Hz dotted), live UTC, band/mode/SPLIT badges, CAT pill with rig selector and clickable Azimuth pill (rotor TODO) - Settings tree: Profiles (Log4OM-style manager), Station Information (edits the active profile), unified Callsign Lookup with Test buttons, Bands/Modes lists, CAT - Worked-before matrix (band × mode × class) with new-DXCC highlighting - ADIF import from menu + Maintenance > Refresh cty.dat Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogFooter,
|
||||
AlertDialogTitle, AlertDialogDescription,
|
||||
AlertDialogAction, AlertDialogCancel,
|
||||
} from '@/components/ui/alert-dialog';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { buttonVariants } from '@/components/ui/button';
|
||||
|
||||
interface Props {
|
||||
title?: string;
|
||||
message: string;
|
||||
confirmLabel?: string;
|
||||
cancelLabel?: string;
|
||||
danger?: boolean;
|
||||
/** When set, user must type this exact phrase before the confirm button enables. */
|
||||
confirmPhrase?: string;
|
||||
onConfirm: () => void;
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
export function ConfirmDialog({
|
||||
title = 'Confirm',
|
||||
message,
|
||||
confirmLabel = 'Confirm',
|
||||
cancelLabel = 'Cancel',
|
||||
danger = false,
|
||||
confirmPhrase = '',
|
||||
onConfirm,
|
||||
onCancel,
|
||||
}: Props) {
|
||||
const [typed, setTyped] = useState('');
|
||||
const enabled = confirmPhrase === '' || typed === confirmPhrase;
|
||||
|
||||
return (
|
||||
<AlertDialog open onOpenChange={(o) => { if (!o) onCancel(); }}>
|
||||
<AlertDialogContent
|
||||
onKeyDown={(e) => { if (e.key === 'Enter' && enabled) onConfirm(); }}
|
||||
>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>{title}</AlertDialogTitle>
|
||||
<AlertDialogDescription>{message}</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
|
||||
{confirmPhrase && (
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="cd-input" className="text-xs text-foreground">
|
||||
Type{' '}
|
||||
<code className="bg-destructive/10 text-destructive font-mono font-bold px-1.5 py-0.5 rounded">
|
||||
{confirmPhrase}
|
||||
</code>{' '}
|
||||
to confirm:
|
||||
</Label>
|
||||
<Input
|
||||
id="cd-input"
|
||||
autoFocus
|
||||
value={typed}
|
||||
onChange={(e) => setTyped(e.target.value)}
|
||||
className="font-mono"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>{cancelLabel}</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
disabled={!enabled}
|
||||
className={cn(
|
||||
danger && buttonVariants({ variant: 'destructive' }),
|
||||
!enabled && 'opacity-50 pointer-events-none',
|
||||
)}
|
||||
onClick={() => { if (enabled) onConfirm(); }}
|
||||
>
|
||||
{confirmLabel}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user