"use client"; import { useEffect, useRef, useState } from "react"; import { createPortal } from "react-dom"; import { useRouter } from "next/navigation"; import { X, Upload, Users, AlertCircle, Check, Loader2, Star, Gem } from "lucide-react"; import { previewActressImport, commitActressImport, type ImportResult, } from "@/app/actions/actressImport"; import { listActressCategoriesAction } from "@/app/actions/actressCategoriesQuery"; import type { ActressCategory } from "@/lib/db/queries"; import { cn } from "@/lib/utils"; interface Props { onClose: () => void; } export function ActressImportDialog({ onClose }: Props) { const router = useRouter(); const [text, setText] = useState(""); const [preview, setPreview] = useState(null); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); const [categories, setCategories] = useState([]); const [defaultCategoryId, setDefaultCategoryId] = useState(null); const fileRef = useRef(null); const debounceRef = useRef(null); const previewSeq = useRef(0); useEffect(() => { const onKey = (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, [onClose]); useEffect(() => { listActressCategoriesAction().then(setCategories).catch(() => {}); }, []); const favoriteCat = categories.find((c) => c.slug === "favorite"); const vipCat = categories.find((c) => c.slug === "vip"); // Debounced preview as the user types. useEffect(() => { if (debounceRef.current) clearTimeout(debounceRef.current); const requestText = text; const requestId = ++previewSeq.current; if (!requestText.trim()) { setPreview(null); setError(null); return; } debounceRef.current = setTimeout(async () => { try { const r = await previewActressImport(requestText); if (previewSeq.current !== requestId) return; setPreview(r); setError(null); } catch (e) { if (previewSeq.current !== requestId) return; setError((e as Error).message); } }, 300); return () => { if (debounceRef.current) clearTimeout(debounceRef.current); }; }, [text]); async function onFile(file: File) { const t = await file.text(); setText(t); } async function commit() { if (!preview || preview.added === 0) return; setBusy(true); try { const defaults = defaultCategoryId != null ? [defaultCategoryId] : []; await commitActressImport(text, defaults); router.refresh(); onClose(); } catch (e) { setError((e as Error).message); } finally { setBusy(false); } } if (typeof document === "undefined") return null; return createPortal(
{ if (e.target === e.currentTarget) onClose(); }} >
Import Actresses
One name per line. Optionally Name | alt names | categories
.txt, .csv (one per line) { const f = e.target.files?.[0]; if (f) onFile(f); e.target.value = ""; }} />