115 lines
3.7 KiB
TypeScript
115 lines
3.7 KiB
TypeScript
import { type ReactNode } from 'react'
|
|
import { useNavigate, useSearchParams, Navigate } from 'react-router'
|
|
import { ArrowLeft, Package, ClipboardList } from 'lucide-react'
|
|
import { cn, interactive } from '@/lib'
|
|
import { MainLayout, MainHeaderBar } from '@/layouts'
|
|
import { ChatContainer, ItemSection, RemainingTime } from '@/features/chat'
|
|
import { MobileDrawer } from '@/features/chat/components/MobileDrawer'
|
|
import { MenuSection } from '@/features/chat/components/menu/MenuSection'
|
|
import { useChatUiStore } from '@/features/chat/stores/useChatUiStore'
|
|
import { SidebarFooter } from '@/features/auth'
|
|
|
|
export function ChatPage() {
|
|
const [searchParams] = useSearchParams()
|
|
const sessionId = searchParams.get('session_id') ?? ''
|
|
const drawer = useChatUiStore((s) => s.drawer)
|
|
const close = useChatUiStore((s) => s.close)
|
|
|
|
// session_id 없이 진입하면 목록으로 되돌린다.
|
|
if (!sessionId) return <Navigate to="/list" replace />
|
|
|
|
return (
|
|
<>
|
|
<MainLayout
|
|
sidebarWidth="chat"
|
|
sidebar={<Sidebar />}
|
|
header={
|
|
<MainHeaderBar className="px-4 sm:px-6 min-[1180px]:px-8">
|
|
<div className="flex w-full items-center justify-between gap-2">
|
|
<div className="flex min-w-0 items-center gap-2">
|
|
<BackToListButton />
|
|
<span className="hidden h-4 w-px bg-border lg:inline-block" />
|
|
<span className="hidden size-2 shrink-0 rounded-full bg-brand-600 animate-pulse lg:inline-block" />
|
|
<span className="hidden truncate text-sm font-bold text-neutral-90 lg:inline">
|
|
실시간 구매 협상 채널
|
|
</span>
|
|
</div>
|
|
<div className="flex items-center gap-1.5">
|
|
<PanelToggle icon={<Package className="size-4" />} label="상품" panel="product" />
|
|
<PanelToggle icon={<ClipboardList className="size-4" />} label="현황" panel="status" />
|
|
<RemainingTime />
|
|
</div>
|
|
</div>
|
|
</MainHeaderBar>
|
|
}
|
|
>
|
|
<ChatContainer sessionId={sessionId} />
|
|
</MainLayout>
|
|
|
|
{/* 모바일 드로어 (lg 미만) */}
|
|
<MobileDrawer open={drawer === 'product'} onClose={close} side="left" title="상품 정보">
|
|
<ItemSection />
|
|
<SidebarFooter />
|
|
</MobileDrawer>
|
|
<MobileDrawer open={drawer === 'status'} onClose={close} side="right" title="협상 현황">
|
|
<MenuSection variant="drawer" />
|
|
</MobileDrawer>
|
|
</>
|
|
)
|
|
}
|
|
|
|
// 헤더 좌상단: 목록으로 돌아가기 (텍스트 + ← 아이콘)
|
|
function BackToListButton() {
|
|
const navigate = useNavigate()
|
|
return (
|
|
<button
|
|
type="button"
|
|
onClick={() => navigate('/list')}
|
|
className={cn(
|
|
'flex shrink-0 items-center gap-1.5 rounded-lg px-2 py-1.5 text-sm font-bold text-neutral-70 hover:bg-neutral-10',
|
|
interactive,
|
|
)}
|
|
>
|
|
<ArrowLeft className="size-4" />
|
|
<span className="hidden sm:inline">목록으로 돌아가기</span>
|
|
</button>
|
|
)
|
|
}
|
|
|
|
// 헤더 우측: 상품/현황 패널 드로어 토글 (모바일 전용)
|
|
function PanelToggle({
|
|
icon,
|
|
label,
|
|
panel,
|
|
}: {
|
|
icon: ReactNode
|
|
label: string
|
|
panel: 'product' | 'status'
|
|
}) {
|
|
const open = useChatUiStore((s) => s.open)
|
|
return (
|
|
<button
|
|
type="button"
|
|
onClick={() => open(panel)}
|
|
className={cn(
|
|
'flex items-center gap-1 rounded-lg border border-border px-2 py-1.5 text-xs font-bold text-neutral-70 hover:bg-neutral-10 lg:hidden',
|
|
interactive,
|
|
)}
|
|
>
|
|
{icon}
|
|
<span className="hidden min-[480px]:inline">{label}</span>
|
|
</button>
|
|
)
|
|
}
|
|
|
|
function Sidebar() {
|
|
return (
|
|
<>
|
|
<ItemSection />
|
|
<SidebarFooter />
|
|
</>
|
|
)
|
|
}
|
|
|
|
export default ChatPage
|