import { useEffect, useRef, memo } from 'react'
import { useMeQuery } from '@/apis'
import { useChatStore } from '@/features/chat/stores/useChatStore'
import { useChatInitStore } from '@/features/chat/stores/useChatInitStore'
import type { ChatMessage as ChatMessageType } from '@/features/chat/types'
import { renderEmphasis } from '@/features/chat/lib/emphasis'
import { Indicator } from '@/features/chat/components/templates/Indicator'
import { Summary } from '@/features/chat/components/templates/Summary'
import { ExtraInfoForm } from '@/features/chat/components/templates/ExtraInfoForm'
import { BidSummary } from '@/features/chat/components/templates/BidSummary'
import { RejectRSP } from '@/features/chat/components/templates/RejectRSP'
import { RejectCM } from '@/features/chat/components/templates/RejectCM'
const AI_LABEL = '아이마켓코리아 (구매 MD)'
export function ChatMessage() {
return (
{/* pt-[64px]: 첫 메시지를 우측 협상절차 카드 상단과 맞추되, 스크롤 시 여백도 함께 밀려 올라가도록 스크롤 컨테이너 안쪽에 둔다 */}
)
}
function ChatList() {
const bottomRef = useRef(null)
const chats = useChatStore((s) => s.messages)
const isLoading = useChatStore((s) => s.isLoading)
// 메시지 추가/타이핑 표시 시 항상 맨 아래로 스크롤
useEffect(() => {
bottomRef.current?.scrollIntoView({ behavior: 'smooth' })
}, [chats, isLoading])
if (!chats || chats.length === 0) {
return (
)
}
return (
{chats.map((message, index) => (
))}
{isLoading &&
}
)
}
// agent 응답을 기다리는 동안(협상 중) 대화 흐름에 표시하는 타이핑 인디케이터(AI 말풍선 톤).
function TypingBubble() {
return (
)
}
const MessageItem = memo(function MessageItem({
message,
messages,
currentIndex,
}: {
message: ChatMessageType
messages: ChatMessageType[]
currentIndex: number
}) {
const isBot = message.sender === 'bot'
// 직전이 reject 폼이면 사용자 답변 말풍선은 숨긴다(폼 자체가 답변을 담고 있음)
if (!isBot && currentIndex > 0) {
const prev = messages[currentIndex - 1]
if (prev?.bot_chat_type === 'rejectRSP' || prev?.bot_chat_type === 'rejectCM') {
return null
}
}
return isBot ? :
})
const BotMessage = memo(function BotMessage({ message }: { message: ChatMessageType }) {
// 인디케이터는 이번 방문에서 '협상중'이었던 세션에서만 표시.
const showIndicator = useChatInitStore((s) => s.wasInProgress)
const hasScript = Boolean(message.script && message.script.trim())
return (
{AI_LABEL}
{hasScript && (
{renderEmphasis(message.script || '')}
)}
{showIndicator && message.bot_chat_type === 'indicator' && message.indicator_value != null && (
)}
{message.bot_chat_type === 'summaryRSP' && message.summary && (
<>
>
)}
{message.bot_chat_type === 'summaryCM' && message.summary && (
)}
{message.bot_chat_type === 'rejectRSP' && }
{message.bot_chat_type === 'rejectCM' && }
)
})
const UserMessage = memo(function UserMessage({ text }: { text: string }) {
const { data: user } = useMeQuery()
const label = `${user?.supplierName ?? '공급사'} (나)`
return (
)
})