Files
initiative/apps/web/src/components/import-method-dialog.tsx
Lukas f4fb69dbc7
All checks were successful
CI / check (push) Successful in 1m13s
CI / build-image (push) Has been skipped
Add jsinspect-plus structural duplication gate, extract shared helpers
Add jsinspect-plus (AST-based structural duplication detector) to pnpm
check with threshold 50 / min 3 instances. Fix all findings:

- Extract condition icon/color maps to shared condition-styles.ts
- Extract useClickOutside hook (5 components)
- Extract dispatchAction + resolveAndRename in use-encounter
- Extract runEncounterAction in application layer (13 use cases)
- Extract findCombatant helper in domain (9 functions)
- Extract TraitSection in stat-block (4 trait rendering blocks)
- Extract DialogHeader in dialog.tsx (4 dialogs)

Net result: -263 lines across 40 files.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 02:16:54 +01:00

115 lines
3.1 KiB
TypeScript

import { ClipboardPaste, FileUp } from "lucide-react";
import { useCallback, useEffect, useRef, useState } from "react";
import { Button } from "./ui/button.js";
import { Dialog, DialogHeader } from "./ui/dialog.js";
interface ImportMethodDialogProps {
open: boolean;
onSelectFile: () => void;
onSubmitClipboard: (text: string) => void;
onClose: () => void;
}
export function ImportMethodDialog({
open,
onSelectFile,
onSubmitClipboard,
onClose,
}: Readonly<ImportMethodDialogProps>) {
const textareaRef = useRef<HTMLTextAreaElement>(null);
const [mode, setMode] = useState<"pick" | "paste">("pick");
const [pasteText, setPasteText] = useState("");
const handleClose = useCallback(() => {
setMode("pick");
setPasteText("");
onClose();
}, [onClose]);
useEffect(() => {
if (!open) {
setMode("pick");
setPasteText("");
}
}, [open]);
useEffect(() => {
if (mode === "paste") {
textareaRef.current?.focus();
}
}, [mode]);
return (
<Dialog open={open} onClose={handleClose} className="w-80">
<DialogHeader title="Import Encounter" onClose={handleClose} />
{mode === "pick" && (
<div className="flex flex-col gap-2">
<button
type="button"
className="flex items-center gap-3 rounded-lg border border-border px-4 py-3 text-left text-foreground text-sm hover:bg-hover-neutral-bg"
onClick={() => {
handleClose();
onSelectFile();
}}
>
<FileUp className="h-5 w-5 text-muted-foreground" />
<div>
<div className="font-medium">From file</div>
<div className="text-muted-foreground text-xs">
Upload a JSON file
</div>
</div>
</button>
<button
type="button"
className="flex items-center gap-3 rounded-lg border border-border px-4 py-3 text-left text-foreground text-sm hover:bg-hover-neutral-bg"
onClick={() => setMode("paste")}
>
<ClipboardPaste className="h-5 w-5 text-muted-foreground" />
<div>
<div className="font-medium">Paste content</div>
<div className="text-muted-foreground text-xs">
Paste JSON content directly
</div>
</div>
</button>
</div>
)}
{mode === "paste" && (
<div className="flex flex-col gap-3">
<textarea
ref={textareaRef}
value={pasteText}
onChange={(e) => setPasteText(e.target.value)}
placeholder="Paste exported JSON here..."
className="h-32 w-full resize-none rounded-md border border-border bg-background px-3 py-2 font-mono text-foreground text-xs placeholder:text-muted-foreground focus:border-accent focus:outline-none"
/>
<div className="flex justify-end gap-2">
<Button
type="button"
variant="ghost"
onClick={() => {
setMode("pick");
setPasteText("");
}}
>
Back
</Button>
<Button
type="button"
disabled={pasteText.trim().length === 0}
onClick={() => {
const text = pasteText;
handleClose();
onSubmitClipboard(text);
}}
>
Import
</Button>
</div>
</div>
)}
</Dialog>
);
}