o2o-negosium-original/front/src/features/chat/components/menu/MDInformation.tsx
민헌 147f9dd007 feat(front): 협상 채팅 페이지 (mock UI)
- KT-NEGOWIZ /chat 을 현재 아키텍처(Vite·React Router·feature 구조)로 이식
- 사이드 ItemSection(+이미지 확대), 헤더 잔여시간, 채팅(메시지/템플릿/입력), 메뉴 섹션
- 메시지 템플릿: 협상 성공률·협상요약·투찰요약·재협상/재견적 폼
- zustand 스토어 + mock 데이터(정적 쇼케이스, 제출 시 로컬 append)
- Next.js·KT 의존 제거(next/image·navigation, 로고·서비스명·연락처, 팝업 보류)
- ChatPage 배선: ChatContainer / ItemSection / RemainingTime + auth SidebarFooter 재사용

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 14:38:04 +09:00

85 lines
2.8 KiB
TypeScript

import { useMemo } from 'react'
import { ChevronDown } from 'lucide-react'
import { cn } from '@/lib'
import { useChatInitStore } from '@/features/chat/stores/useChatInitStore'
interface Props {
isOpen: boolean
setIsOpen: (isOpen: boolean) => void
}
export function MDInformation({ isOpen, setIsOpen }: Props) {
const { quotation_memo } = useChatInitStore()
const memoArray = useMemo(() => {
if (!quotation_memo?.trim()) return []
return quotation_memo
.split(/\r?\n+/)
.map((s) => s.trim())
.filter(Boolean)
}, [quotation_memo])
if (memoArray.length === 0) return null
return (
<div className="flex flex-col w-full min-w-[200px]">
<div className="bg-white rounded-[28px]">
<div className={cn('flex items-center w-full h-[4.75rem] px-4 pl-6', isOpen ? 'pt-6 pb-3' : 'py-6')}>
<div className="flex items-center flex-1 justify-between">
<div className="menu-title text-neutral-80">MD </div>
<button
type="button"
aria-expanded={isOpen}
onClick={() => setIsOpen(!isOpen)}
className="flex items-center justify-center w-10 h-10 cursor-pointer text-neutral-70"
>
<ChevronDown size={20} className={cn('transition-transform duration-300', !isOpen && 'rotate-180')} />
</button>
</div>
</div>
<div
className={cn(
'flex flex-col items-start gap-[19px] w-full px-6 overflow-hidden transition-all duration-300 ease-in-out',
isOpen ? 'max-h-[200px] pb-8 opacity-100' : 'max-h-0 pb-0 opacity-0',
)}
>
<div className={cn('flex flex-col gap-2 items-start self-stretch max-h-[200px] break-keep', isOpen ? 'overflow-y-auto' : 'overflow-hidden')}>
{memoArray.map((item, i) => (
<div key={i} className="flex items-start gap-2 body-3 text-neutral-70">
<span className="flex-shrink-0"></span>
<div className="flex-1 whitespace-pre-wrap">{linkText(item)}</div>
</div>
))}
</div>
</div>
</div>
</div>
)
}
function linkText(text: string) {
const urlRegex = /(https?:\/\/[^\s]+)/g
const isUrl = (s: string) => /^https?:\/\/[^\s]+$/.test(s)
const parts = text.split(urlRegex)
return (
<div className="body-3 text-neutral-70">
{parts.map((part, i) =>
isUrl(part) ? (
<a
key={i}
href={part}
target="_blank"
rel="noopener noreferrer"
className="block text-info underline cursor-pointer hover:brightness-95 transition-all"
>
</a>
) : (
<span key={i}>{i > 0 && isUrl(parts[i - 1]) ? part.replace(/^(\s)/, '') : part}</span>
),
)}
</div>
)
}