"use client"; import React, { useState, useEffect, useRef } from "react"; import { Send, Square, Sparkles, Pill, Search, Command } from "lucide-react"; import { cn } from "@duoc-thu/ui"; interface ComposerProps { onSubmit: (query: string) => void; isLoading?: boolean; onStop?: () => void; initialValue?: string; className?: string; } const SAMPLE_SUGGESTIONS = [ "Liều dùng Paracetamol người lớn và trẻ em theo cân nặng", "Chống chỉ định và tác dụng không mong muốn của Amoxicillin", "Tương tác thuốc giữa Metformin và thuốc cản quang", "Thận trọng khi dùng Aspirin cho bệnh nhân loét dạ dày", "Hướng dẫn liều dùng Ibuprofen và giới hạn tối đa ngày", ]; export function Composer({ onSubmit, isLoading = false, onStop, initialValue = "", className, }: ComposerProps) { const [value, setValue] = useState(initialValue); const [suggestions, setSuggestions] = useState([]); const [showSuggestions, setShowSuggestions] = useState(false); const [selectedIndex, setSelectedIndex] = useState(-1); const inputRef = useRef(null); const dropdownRef = useRef(null); useEffect(() => { if (initialValue) { setValue(initialValue); } }, [initialValue]); // Fetch suggestions from API route when query length > 1 useEffect(() => { const term = value.trim(); if (term.length < 2) { setSuggestions([]); setShowSuggestions(false); return; } const timer = setTimeout(async () => { try { const res = await fetch(`/api/suggest?q=${encodeURIComponent(term)}`); if (res.ok) { const data = await res.json(); if (data?.suggestions && Array.isArray(data.suggestions) && data.suggestions.length > 0) { setSuggestions(data.suggestions); setShowSuggestions(true); return; } } // Fallback filter local suggestions const filtered = SAMPLE_SUGGESTIONS.filter((s) => s.toLowerCase().includes(term.toLowerCase()) ); setSuggestions(filtered); setShowSuggestions(filtered.length > 0); } catch { setSuggestions([]); setShowSuggestions(false); } }, 200); return () => clearTimeout(timer); }, [value]); // Close suggestions on outside click useEffect(() => { function handleClickOutside(event: MouseEvent) { if ( dropdownRef.current && !dropdownRef.current.contains(event.target as Node) && inputRef.current && !inputRef.current.contains(event.target as Node) ) { setShowSuggestions(false); } } document.addEventListener("mousedown", handleClickOutside); return () => document.removeEventListener("mousedown", handleClickOutside); }, []); const handleSubmit = () => { const trimmed = value.trim(); if (!trimmed || isLoading) return; onSubmit(trimmed); setValue(""); setSuggestions([]); setShowSuggestions(false); }; const handleKeyDown = (e: React.KeyboardEvent) => { if (showSuggestions && suggestions.length > 0) { if (e.key === "ArrowDown") { e.preventDefault(); setSelectedIndex((prev) => (prev < suggestions.length - 1 ? prev + 1 : 0)); return; } if (e.key === "ArrowUp") { e.preventDefault(); setSelectedIndex((prev) => (prev > 0 ? prev - 1 : suggestions.length - 1)); return; } if (e.key === "Enter" && selectedIndex >= 0) { e.preventDefault(); setValue(suggestions[selectedIndex]); setShowSuggestions(false); setSelectedIndex(-1); return; } } if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); handleSubmit(); } }; return (
{/* Autocomplete Suggestions Dropdown */} {showSuggestions && suggestions.length > 0 && (
Gợi ý tra cứu Dược thư
{suggestions.map((item, idx) => ( ))}
)} {/* Main Composer Box */}