"use client"; import { Loader2, Sparkles } from "lucide-react"; import { useCallback, useMemo, useState } from "react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Textarea } from "@/components/ui/textarea"; import { ItemImageCapture, type ProcessedImage } from "./ItemImageCapture"; interface ItemAiAssistantProps { locale: string; categories: string[]; serverOptions: string[]; onGenerated: (result: { json: unknown; prefill: ItemAiPrefill }) => void; } export interface ItemAiPrefill { name?: string; description?: string; category?: string; rarity?: string | null; servers?: string[] | string; tags?: string[] | string; metadata?: Record; } export function ItemAiAssistant({ locale, categories, serverOptions, onGenerated, }: ItemAiAssistantProps) { const [name, setName] = useState(""); const [description, setDescription] = useState(""); const [scope, setScope] = useState<"global" | "server">("global"); const [server, setServer] = useState(serverOptions[0] ?? ""); const [images, setImages] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [rawJson, setRawJson] = useState(null); const [logs, setLogs] = useState([]); // Habilita geração se houver ao menos uma imagem válida OU nome+descrição preenchidos const canGenerate = useMemo(() => { const hasValidImage = images.length > 0; const hasTextInputs = name.trim() && description.trim(); return Boolean(hasValidImage || hasTextInputs); }, [images.length, name, description]); const disabled = useMemo(() => !canGenerate, [canGenerate]); const handleGenerate = useCallback(async () => { if (disabled || loading) return; setLoading(true); setError(null); setLogs((prev) => [...prev, "Iniciando geração com IA..."]); try { setLogs((prev) => [ ...prev, `Payload: nome='${name.trim()}', escopo='${scope}', imagens=${images.length}, servidor='${scope === "server" ? server : "(global)"}'`, ]); const response = await fetch("/api/admin/items/analyze", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ locale, name, description, scope, server: scope === "server" ? server : null, categories, servers: serverOptions, images: images.map((image) => image.dataUrl), }), }); if (!response.ok) { const data = await response.json().catch(() => null); throw new Error(data?.error || "Falha na análise com IA."); } const data = await response.json(); const structure = data?.structure ?? data; const columns = structure?.columns; setLogs((prev) => [ ...prev, `Resposta recebida. Tamanho ~${JSON.stringify(structure).length} bytes.`, ]); // Heurística: sugerir nomes com base em imagens quando não houver texto detectado const suggestNamesFromImages = (imgs: ProcessedImage[]): string[] => { if (!Array.isArray(imgs) || imgs.length === 0) return []; const first = imgs[0]; const base = first.originalName .replace(/\.[a-zA-Z0-9]+$/, "") .toLowerCase() .replace(/[_\s]+/g, "-"); const common = new Set([ "image", "img", "screenshot", "captura", "photo", "foto", "unknown", ]); const orientation = first.height > first.width ? "retrato" : "paisagem"; const suggestions: string[] = []; if (base && !common.has(base)) suggestions.push(base); suggestions.push(`${orientation}-1`); suggestions.push("foto-1"); return Array.from(new Set(suggestions)).slice(0, 3); }; const autoNameDetected = typeof columns?.name === "string" && Boolean(String(columns.name).trim()); const autoDescDetected = typeof columns?.description === "string" && Boolean(String(columns.description).trim()); const origemDados: string = autoNameDetected || autoDescDetected ? "automatico" : "nao_detectado"; const sugestoesNome = !autoNameDetected ? suggestNamesFromImages(images) : []; // Estrutura final com campos obrigatórios, origem e sugestões const finalStructure = { ...structure, columns: { ...columns, origem_dados: origemDados, confirmar_nome: autoNameDetected ? false : undefined, mensagens: { obrigatorio: ["nome"], dicas: autoNameDetected ? "Confirme o nome sugerido ou ajuste se necessário." : "Informe um nome para o item. Descrição é opcional, mas ajuda.", }, sugestoes: sugestoesNome.length > 0 ? { nome: sugestoesNome } : undefined, }, }; setRawJson(JSON.stringify(finalStructure, null, 2)); const prefill: ItemAiPrefill = { name: autoNameDetected ? (columns?.name as string) : undefined, description: autoDescDetected ? (columns?.description as string) : undefined, metadata: typeof columns === "object" && columns !== null ? { ...columns, origem_dados: origemDados, confirmar_nome: autoNameDetected ? false : undefined, sugestoes: sugestoesNome.length ? { nome: sugestoesNome } : undefined, } : { origem_dados: origemDados, confirmar_nome: autoNameDetected ? false : undefined, sugestoes: sugestoesNome.length ? { nome: sugestoesNome } : undefined, }, }; if (columns) { if (typeof columns.category === "string") { prefill.category = columns.category; } if (Array.isArray(columns.servers)) { prefill.servers = columns.servers as string[]; } if (typeof columns.servers === "string") { prefill.servers = columns.servers; } if (Array.isArray(columns.tags)) { prefill.tags = columns.tags as string[]; } if (typeof columns.tags === "string") { prefill.tags = columns.tags; } if (columns.rarity && typeof columns.rarity === "string") { prefill.rarity = columns.rarity; } } setLogs((prev) => [ ...prev, "Sugestões de preenchimento geradas. Aguardando confirmação do usuário.", ]); onGenerated({ json: finalStructure, prefill }); } catch (err) { console.error(err); setError( err instanceof Error ? err.message : "Erro ao processar análise de IA.", ); setLogs((prev) => [ ...prev, `Erro: ${err instanceof Error ? err.message : String(err)}`, ]); } finally { setLoading(false); setLogs((prev) => [...prev, "Processo de geração concluído."]); } }, [ categories, description, disabled, images, locale, loading, name, onGenerated, scope, server, serverOptions, ]); return (

Assistente IA para registro de item

Cole prints e descreva o item. A IA vai sugerir a tabela, campos e metadados. Você pode ajustar os dados antes de salvar.

Até 2 imagens · Compressão automática · Suporte a Ctrl+V
setName(event.target.value)} placeholder="Ex.: Final Fate" />
{scope === "server" && (
)}