rigs completed

This commit is contained in:
2026-05-28 18:35:22 +02:00
parent d3c9982c66
commit e8cac569e3
26 changed files with 3834 additions and 391 deletions
@@ -0,0 +1,66 @@
import { useEffect, useState } from 'react';
import { CheckCircle2, Loader2, XCircle } from 'lucide-react';
import { EventsOn } from '../../wailsjs/runtime/runtime';
type Step = {
id: string;
label: string;
status: 'pending' | 'running' | 'done' | 'error';
detail?: string;
};
// ShutdownProgress is a full-screen overlay that appears while HamLog is
// running its close-time tasks (backup, future LoTW upload, ...). It
// listens for `shutdown:start` / `shutdown:update` / `shutdown:done`
// events from the backend and renders a checklist that updates as each
// task completes. The backend triggers wruntime.Quit() once everything
// is finished, so this component never has to dismiss itself.
export function ShutdownProgress() {
const [steps, setSteps] = useState<Step[] | null>(null);
useEffect(() => {
const u1 = EventsOn('shutdown:start', (s: Step[]) => setSteps(s ?? []));
const u2 = EventsOn('shutdown:update', (s: Step[]) => setSteps(s ?? []));
const u3 = EventsOn('shutdown:done', (s: Step[]) => setSteps(s ?? []));
return () => { u1?.(); u2?.(); u3?.(); };
}, []);
if (!steps) return null;
return (
<div className="fixed inset-0 z-[9999] flex items-center justify-center bg-black/40 backdrop-blur-sm">
<div className="bg-card border border-border rounded-lg shadow-xl p-6 min-w-[360px] max-w-[480px]">
<div className="text-sm font-semibold mb-3 text-foreground">Closing HamLog</div>
<div className="space-y-2">
{steps.length === 0 ? (
<div className="text-xs text-muted-foreground italic">Nothing to do, exiting.</div>
) : steps.map((s) => (
<div key={s.id} className="flex items-start gap-2 text-sm">
<div className="mt-0.5 w-4 flex items-center justify-center">
{s.status === 'done' && <CheckCircle2 className="size-4 text-emerald-600" />}
{s.status === 'running' && <Loader2 className="size-4 animate-spin text-primary" />}
{s.status === 'error' && <XCircle className="size-4 text-rose-600" />}
{s.status === 'pending' && <span className="size-2 rounded-full bg-muted-foreground/40" />}
</div>
<div className="flex-1">
<div className={
s.status === 'done' ? 'text-foreground'
: s.status === 'error' ? 'text-rose-700 font-medium'
: s.status === 'pending' ? 'text-muted-foreground'
: 'text-foreground font-medium'
}>
{s.label}
</div>
{s.detail && (
<div className="text-[11px] text-muted-foreground mt-0.5 font-mono break-all">
{s.detail}
</div>
)}
</div>
</div>
))}
</div>
</div>
</div>
);
}