"use client"; import { useEffect, useRef, useState } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { Bot } from "lucide-react"; import { MessageBubble } from "./MessageBubble"; import { FeedbackWidget } from "./FeedbackWidget"; import { TypingIndicator } from "./TypingIndicator"; import { useChatStore } from "@/stores/chatStore"; import { useAuthStore } from "@/stores/authStore"; import { getChatbotConfig } from "@/lib/chatbotConfig"; import type { ChatMessage, SummaryPack } from "@/types"; function getAudioText(msg: ChatMessage): string | null { if (msg.audio_text) return msg.audio_text; const summary = msg.data?.datasets?.find( (d): d is SummaryPack => "chart_type" in d && d.chart_type === "summary" ); return summary?.audio_text ?? null; } interface ChatWindowProps { showFeedback?: boolean; onFeedbackDismiss?: () => void; } export function ChatWindow({ showFeedback, onFeedbackDismiss }: ChatWindowProps) { const bottomRef = useRef(null); const lastMessageRef = useRef(null); const prevMessageCount = useRef(0); const prevIsSending = useRef(false); const isFirstRender = useRef(true); const messages = useChatStore((s) => s.getActiveMessages()); const isSending = useChatStore((s) => s.isSending); const userInfo = useAuthStore((s) => s.userInfo); const [chatbotConfig] = useState(() => getChatbotConfig()); // Scroll to bottom when typing indicator appears useEffect(() => { if (isSending !== prevIsSending.current) { prevIsSending.current = isSending; if (isSending) { bottomRef.current?.scrollIntoView({ behavior: "smooth" }); } } }, [isSending]); // On initial mount scroll to bottom; on new AI message scroll to its top useEffect(() => { if (isFirstRender.current) { isFirstRender.current = false; prevMessageCount.current = messages.length; bottomRef.current?.scrollIntoView({ behavior: "smooth" }); return; } if (messages.length > prevMessageCount.current) { const lastMsg = messages[messages.length - 1]; if (lastMsg?.role === "ai") { lastMessageRef.current?.scrollIntoView({ behavior: "smooth", block: "start" }); } else { bottomRef.current?.scrollIntoView({ behavior: "smooth" }); } } prevMessageCount.current = messages.length; }, [messages]); if (messages.length === 0) { return (
{/* Welcome state */}

Halo, {userInfo?.fullname?.split(" ")[0] || ""}! 👋

{chatbotConfig.description}

{[ { icon: "📊", title: "Analisis Data", desc: "PA, Bad Actor, Pareto, dll." }, { icon: "📖", title: "Knowledge Base", desc: "SOP, Handbook & Dokumen" }, { icon: "🔍", title: "Pareto & Trend", desc: "Identifikasi pola masalah" }, { icon: "📋", title: "Report Ready", desc: "Grafik & insight otomatis" }, ].map((item) => (
{item.icon}

{item.title}

{item.desc}

))}
); } return (
{messages.map((message, i) => ( {showFeedback && i === messages.length - 1 && message.role === "ai" && ( )} ))} {/* Typing indicator */} {isSending && }
); }