ema-frontend-demo / src /components /chat /ChatInput.tsx
ishaq101's picture
Refactor API calls to direct backend integration, add chatbot configuration system, and improve UI components
db983c6
"use client";
import { useState, useRef, useCallback } from "react";
import { SendHorizontal, Square } from "lucide-react";
import { cn } from "@/lib/utils";
import { useChatStore } from "@/stores/chatStore";
import { getChatbotConfig } from "@/lib/chatbotConfig";
interface ChatInputProps {
onSend: (message: string) => void;
disabled?: boolean;
}
export function ChatInput({ onSend, disabled }: ChatInputProps) {
const [value, setValue] = useState("");
const textareaRef = useRef<HTMLTextAreaElement>(null);
const { isSending } = useChatStore();
const [chatbotConfig] = useState(() => getChatbotConfig());
const autoResize = useCallback(() => {
const ta = textareaRef.current;
if (!ta) return;
ta.style.height = "auto";
ta.style.height = Math.min(ta.scrollHeight, 180) + "px";
}, []);
function handleChange(e: React.ChangeEvent<HTMLTextAreaElement>) {
setValue(e.target.value);
autoResize();
}
function handleKeyDown(e: React.KeyboardEvent<HTMLTextAreaElement>) {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
handleSubmit();
}
}
function handleSubmit() {
const trimmed = value.trim();
if (!trimmed || disabled || isSending) return;
onSend(trimmed);
setValue("");
if (textareaRef.current) {
textareaRef.current.style.height = "auto";
}
}
const isDisabled = disabled || isSending;
const isEmpty = !value.trim();
return (
<div
className={cn(
"flex items-end gap-3 bg-white rounded-2xl border shadow-sm px-4 py-3 transition-all duration-200",
isDisabled
? "border-neutral-200 opacity-80"
: "border-neutral-200 focus-within:border-brand-green focus-within:shadow-md focus-within:shadow-brand-green/10"
)}
>
<textarea
ref={textareaRef}
value={value}
onChange={handleChange}
onKeyDown={handleKeyDown}
disabled={isDisabled}
placeholder={
isSending
? `${chatbotConfig.name} sedang menjawab...`
: "Tanya langsung di sini... (Enter untuk kirim, Shift+Enter baris baru)"
}
rows={1}
className={cn(
"flex-1 resize-none bg-transparent text-sm text-neutral-900 placeholder:text-neutral-400 outline-none leading-6",
"disabled:cursor-not-allowed min-h-[24px] max-h-[180px] overflow-y-auto"
)}
/>
<div className="flex items-center gap-2 flex-shrink-0 pb-0.5">
{/* Character hint */}
{value.length > 500 && (
<span className="text-xs text-neutral-400">{value.length}</span>
)}
{/* Send / Stop button */}
<button
onClick={handleSubmit}
disabled={isEmpty || isDisabled}
className={cn(
"w-9 h-9 rounded-xl flex items-center justify-center transition-all duration-200",
isEmpty || isDisabled
? "bg-neutral-100 text-neutral-300 cursor-not-allowed"
: "bg-gradient-to-br from-brand-green-light to-brand-green text-white shadow-md shadow-brand-green/25 hover:brightness-105"
)}
aria-label="Kirim pesan"
>
{isSending ? (
<Square className="h-3.5 w-3.5" />
) : (
<SendHorizontal className="h-4 w-4" />
)}
</button>
</div>
</div>
);
}