Implement the 030-bulk-import-sources feature that adds a one-click bulk import button to load all bestiary sources at once, with real-time progress feedback in the side panel and a toast notification when the panel is closed, plus completion/failure reporting with auto-dismiss on success and persistent display on partial failure, while also hardening the bestiary normalizer to handle variable stat blocks (spell summons with special AC/HP/CR) and skip malformed monster entries gracefully

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lukas
2026-03-10 23:29:34 +01:00
parent c323adc343
commit 94d125d9c4
14 changed files with 850 additions and 106 deletions

View File

@@ -0,0 +1,47 @@
import { X } from "lucide-react";
import { useEffect } from "react";
import { createPortal } from "react-dom";
interface ToastProps {
message: string;
progress?: number;
onDismiss: () => void;
autoDismissMs?: number;
}
export function Toast({
message,
progress,
onDismiss,
autoDismissMs,
}: ToastProps) {
useEffect(() => {
if (autoDismissMs === undefined) return;
const timer = setTimeout(onDismiss, autoDismissMs);
return () => clearTimeout(timer);
}, [autoDismissMs, onDismiss]);
return createPortal(
<div className="fixed bottom-4 left-1/2 z-50 -translate-x-1/2">
<div className="flex items-center gap-3 rounded-lg border border-border bg-card px-4 py-3 shadow-lg">
<span className="text-sm text-foreground">{message}</span>
{progress !== undefined && (
<div className="h-2 w-24 overflow-hidden rounded-full bg-muted">
<div
className="h-full rounded-full bg-primary transition-all"
style={{ width: `${Math.round(progress * 100)}%` }}
/>
</div>
)}
<button
type="button"
onClick={onDismiss}
className="text-muted-foreground hover:text-hover-neutral"
>
<X className="h-3 w-3" />
</button>
</div>
</div>,
document.body,
);
}