Files
OpsLog/frontend/src/components/ConfirmDialog.tsx
T
rouggyandClaude Opus 4.7 7ace2cc602 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]>
2026-05-26 00:16:45 +02:00

83 lines
2.5 KiB
TypeScript

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>
);
}