From 482c8ed97c946a1a9d234b9feb40343eec85d6a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=AF=BC=ED=97=8C?= Date: Mon, 6 Jul 2026 16:37:18 +0900 Subject: [PATCH] =?UTF-8?q?feat(chat):=20=EB=B0=98=EC=9D=91=ED=98=95=20?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83(~1024px)=20+=20=EB=A6=AC?= =?UTF-8?q?=EB=93=9C=ED=83=80=EC=9E=84=20"=EC=9D=BC"=20=ED=91=9C=EC=8B=9C?= =?UTF-8?q?=20+=20=EC=99=84=EB=A3=8C=EC=84=B8=EC=85=98=20=EC=9D=B8?= =?UTF-8?q?=EB=94=94=EC=BC=80=EC=9D=B4=ED=84=B0=20=EC=88=A8=EA=B9=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 반응형: 최소 폭 1350→1024px, max-[1350px]/max-[1180px] 2단계 축소 - 사이드바 350→310→280, 우측 메뉴 360→320→280, 채팅 거터 140/126→80/72→48/40 (헤더 패딩 동기) - 가격·할인율 입력 min-w 480→420→320, 가운데 버튼 패딩·최소폭 축소, 상품목록 버튼 1180px 미만 아이콘만 - 상품 이미지 220→200, 사이드바 정보 행 값 영역 고정 150px → flex-1 - 리드타임: formatLeadTime 헬퍼 신설 — 좌측 상품정보·협상 결과 요약 카드에 "일" 접미 표시 - 인디케이터: init 의 session_status 를 스토어에 적재, 협상중(IN_PROGRESS) 세션에서만 표시 - 사이드바 정보 영역 의미 없는 가로 스크롤 제거 (shrink 금지 컬럼 + overflow-x 자동 계산이 원인) Co-Authored-By: Claude Fable 5 --- frontend/src/apis/chat/chat.type.ts | 1 + .../src/features/chat/components/ChatMessage.tsx | 9 +++++++-- .../src/features/chat/components/ItemImage.tsx | 4 ++-- .../src/features/chat/components/ItemSection.tsx | 9 +++++---- .../src/features/chat/components/UserButton.tsx | 15 ++++++++------- .../features/chat/components/menu/MenuSection.tsx | 2 +- .../chat/components/templates/Summary.tsx | 3 ++- .../src/features/chat/components/userInputs.tsx | 2 +- frontend/src/features/chat/lib/format.ts | 5 +++++ .../src/features/chat/stores/useChatInitStore.ts | 1 + frontend/src/features/chat/types.ts | 1 + frontend/src/layouts/MainLayout.tsx | 6 +++--- frontend/src/pages/ChatPage.tsx | 2 +- 13 files changed, 38 insertions(+), 22 deletions(-) create mode 100644 frontend/src/features/chat/lib/format.ts diff --git a/frontend/src/apis/chat/chat.type.ts b/frontend/src/apis/chat/chat.type.ts index 3d8fcb7..0455fa5 100644 --- a/frontend/src/apis/chat/chat.type.ts +++ b/frontend/src/apis/chat/chat.type.ts @@ -85,6 +85,7 @@ export function mapMessage(w: ChatMessageWire): ChatMessage { export function mapInit(r: ChatInitResponse): ChatInitData { return { session_id: r.session_id, + session_status: r.session_status, item_id: r.item_id, quotation_id: r.quotation_id, item_name: r.item_name, diff --git a/frontend/src/features/chat/components/ChatMessage.tsx b/frontend/src/features/chat/components/ChatMessage.tsx index 363326b..2888fab 100644 --- a/frontend/src/features/chat/components/ChatMessage.tsx +++ b/frontend/src/features/chat/components/ChatMessage.tsx @@ -1,6 +1,8 @@ import { useEffect, useRef, memo } from 'react' import { cn } from '@/lib' import { useChatStore } from '@/features/chat/stores/useChatStore' +import { useChatInitStore } from '@/features/chat/stores/useChatInitStore' +import { SessionStatus } from '@/apis/negotiation/negotiation.type' import type { ChatMessage as ChatMessageType } from '@/features/chat/types' import { Indicator } from '@/features/chat/components/templates/Indicator' import { Summary } from '@/features/chat/components/templates/Summary' @@ -12,7 +14,7 @@ export function ChatMessage() { return (
{/* pt-[64px]: 첫 메시지를 우측 협상절차 카드 상단과 맞추되, 스크롤 시 여백도 함께 밀려 올라가도록 스크롤 컨테이너 안쪽에 둔다 */} -
+
@@ -91,6 +93,9 @@ const MessageItem = memo(function MessageItem({ }) const BotMessage = memo(function BotMessage({ message, isFirst }: { message: ChatMessageType; isFirst?: boolean }) { + // 인디케이터는 진행 중인 협상에서만 표시 — 완료/거부 등 결과 열람 재진입 시에는 값이 와도 숨긴다 + const sessionStatus = useChatInitStore((s) => s.session_status) + const showIndicator = sessionStatus === SessionStatus.IN_PROGRESS return (
@@ -98,7 +103,7 @@ const BotMessage = memo(function BotMessage({ message, isFirst }: { message: Cha
- {message.bot_chat_type === 'indicator' && message.indicator_value != null && ( + {showIndicator && message.bot_chat_type === 'indicator' && message.indicator_value != null && ( )} {message.bot_chat_type === 'summaryRSP' && message.summary && } diff --git a/frontend/src/features/chat/components/ItemImage.tsx b/frontend/src/features/chat/components/ItemImage.tsx index ff88d06..2d7adb0 100644 --- a/frontend/src/features/chat/components/ItemImage.tsx +++ b/frontend/src/features/chat/components/ItemImage.tsx @@ -27,10 +27,10 @@ export function ItemImage() { function Thumb({ src }: { src: string }) { if (src) { - return 상품 이미지 + return 상품 이미지 } return ( -
+
) diff --git a/frontend/src/features/chat/components/ItemSection.tsx b/frontend/src/features/chat/components/ItemSection.tsx index d9744c8..fc65350 100644 --- a/frontend/src/features/chat/components/ItemSection.tsx +++ b/frontend/src/features/chat/components/ItemSection.tsx @@ -3,6 +3,7 @@ import { createPortal } from 'react-dom' import { cn } from '@/lib' import { ItemImage } from '@/features/chat/components/ItemImage' import { useChatInitStore } from '@/features/chat/stores/useChatInitStore' +import { formatLeadTime } from '@/features/chat/lib/format' export function ItemSection() { return ( @@ -80,7 +81,7 @@ function ItemInfo() {
{title}
displayData !== '-' && showTooltip(e, displayData)} onMouseLeave={hideTooltip} > @@ -100,14 +101,14 @@ function ItemInfo() { {item_name}
-
-
+
+
{renderRow('상품코드', item_code, true)} {renderRow('단가', formattedPrice, true)} {renderRow('모델명', item_model_name, true)} {renderRow('제조사', item_maker_name)} {renderRow('최소주문수량', item_min_order_quantity)} - {renderRow('리드타임', item_lead_time)} + {renderRow('리드타임', formatLeadTime(item_lead_time))}
규격
diff --git a/frontend/src/features/chat/components/UserButton.tsx b/frontend/src/features/chat/components/UserButton.tsx index 480c81a..c2fc1d2 100644 --- a/frontend/src/features/chat/components/UserButton.tsx +++ b/frontend/src/features/chat/components/UserButton.tsx @@ -6,14 +6,15 @@ import { Percent, Price } from '@/features/chat/components/userInputs' import { GO_TO_LIST_TEXT } from '@/features/chat/lib/userButtonConfig' import type { UserButtonConfig } from '@/features/chat/types' +// max-[1180px]: 채팅 폭이 좁아지면 버튼 패딩·최소폭 축소, 상품목록은 아이콘만 남긴다 const style = { goToList: - 'flex w-[159px] h-[48px] bg-neutral-40 hover:brightness-[0.97] rounded-[999px] items-center justify-center title-2 text-neutral-80 cursor-pointer gap-2 transition-all ease-out hover:scale-[1.01]', + 'flex w-[159px] max-[1180px]:w-[48px] h-[48px] bg-neutral-40 hover:brightness-[0.97] rounded-[999px] items-center justify-center title-2 text-neutral-80 cursor-pointer gap-2 max-[1180px]:gap-0 transition-all ease-out hover:scale-[1.01]', black: - 'flex min-w-[120px] px-[32px] h-[48px] bg-primary hover:brightness-[0.97] rounded-[999px] items-center justify-center title-2 text-primary-foreground cursor-pointer whitespace-nowrap transition-all duration-200 ease-out hover:scale-[1.01]', - gray: 'flex min-w-[120px] px-[32px] h-[48px] bg-neutral-60 rounded-[999px] items-center justify-center title-2 text-neutral-00 cursor-pointer whitespace-nowrap transition-all ease-out hover:scale-[1.01]', + 'flex min-w-[120px] px-[32px] max-[1180px]:min-w-[100px] max-[1180px]:px-[20px] h-[48px] bg-primary hover:brightness-[0.97] rounded-[999px] items-center justify-center title-2 text-primary-foreground cursor-pointer whitespace-nowrap transition-all duration-200 ease-out hover:scale-[1.01]', + gray: 'flex min-w-[120px] px-[32px] max-[1180px]:min-w-[100px] max-[1180px]:px-[20px] h-[48px] bg-neutral-60 rounded-[999px] items-center justify-center title-2 text-neutral-00 cursor-pointer whitespace-nowrap transition-all ease-out hover:scale-[1.01]', white: - 'flex min-w-[120px] px-[32px] h-[48px] bg-neutral-00 rounded-[999px] items-center justify-center title-2 text-neutral-80 border border-neutral-80 cursor-pointer whitespace-nowrap transition-all ease-out hover:scale-[1.01]', + 'flex min-w-[120px] px-[32px] max-[1180px]:min-w-[100px] max-[1180px]:px-[20px] h-[48px] bg-neutral-00 rounded-[999px] items-center justify-center title-2 text-neutral-80 border border-neutral-80 cursor-pointer whitespace-nowrap transition-all ease-out hover:scale-[1.01]', } export function UserButton({ type, text, textList, priceErrorMessage }: UserButtonConfig) { @@ -59,9 +60,9 @@ function LoadingDots() { function GoToList() { const navigate = useNavigate() return ( - ) } diff --git a/frontend/src/features/chat/components/menu/MenuSection.tsx b/frontend/src/features/chat/components/menu/MenuSection.tsx index 75c5036..c581635 100644 --- a/frontend/src/features/chat/components/menu/MenuSection.tsx +++ b/frontend/src/features/chat/components/menu/MenuSection.tsx @@ -9,7 +9,7 @@ export function MenuSection() { const [isStepOpen, setIsStepOpen] = useState(true) return ( -
+
diff --git a/frontend/src/features/chat/components/templates/Summary.tsx b/frontend/src/features/chat/components/templates/Summary.tsx index 3901ce6..4d9a366 100644 --- a/frontend/src/features/chat/components/templates/Summary.tsx +++ b/frontend/src/features/chat/components/templates/Summary.tsx @@ -1,4 +1,5 @@ import { numberToKorean } from '@/features/chat/lib/koreanNumber' +import { formatLeadTime } from '@/features/chat/lib/format' import type { ChatSummary } from '@/features/chat/types' // 협상 결과 요약 카드 @@ -26,7 +27,7 @@ export function Summary({ data }: { data: ChatSummary }) { - +
diff --git a/frontend/src/features/chat/components/userInputs.tsx b/frontend/src/features/chat/components/userInputs.tsx index fbefd20..d42c183 100644 --- a/frontend/src/features/chat/components/userInputs.tsx +++ b/frontend/src/features/chat/components/userInputs.tsx @@ -24,7 +24,7 @@ function InputWithUnit({ const errorId = useId() return (
-
+
} sidebar={} header={ - + }