From e7a51b1b754d07e5150dbb4ab86a96d186d09fed Mon Sep 17 00:00:00 2001 From: Mina Choi Date: Wed, 22 Jul 2026 08:29:58 +0900 Subject: [PATCH] =?UTF-8?q?[feat]=20negosium/front:=20=ED=98=91=EC=83=81?= =?UTF-8?q?=EB=B0=A9=20=EB=A7=88=EA=B0=90=20=EC=A0=95=EB=B9=84=20=E2=80=94?= =?UTF-8?q?=20=EC=9A=B0=EC=B8=A1=20=ED=8C=A8=EB=84=90=20=EB=82=A9=EC=9E=91?= =?UTF-8?q?=20=EC=84=B9=EC=85=98=C2=B7=EB=AA=A8=EB=B0=94=EC=9D=BC=20?= =?UTF-8?q?=ED=98=91=EC=83=81=EC=A0=88=EC=B0=A8=20=EC=8A=A4=ED=85=9D?= =?UTF-8?q?=EB=B0=94=C2=B7=EC=9D=B4=EC=9A=A9=EA=B0=80=EC=9D=B4=EB=93=9C=20?= =?UTF-8?q?=EB=AA=A8=EB=8B=AC=20=ED=86=A0=EC=8A=A4=ED=99=94=C2=B7=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=20=EC=9D=B4=EC=9A=A9=EA=B0=80=EC=9D=B4=EB=93=9C=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=C2=B7=ED=8F=B0=ED=8A=B8=20=EC=A0=95=EB=A6=AC?= =?UTF-8?q?=C2=B7=EB=A7=88=EA=B0=90=EC=8B=9C=EA=B0=84=20tabular-nums?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../features/chat/components/ChatMessage.tsx | 2 +- .../features/chat/components/ChatSection.tsx | 2 + .../chat/components/MobileStepBar.tsx | 46 ++++++++++++++++++ .../chat/components/RemainingTime.tsx | 2 +- .../features/chat/components/menu/Contact.tsx | 27 ++--------- .../chat/components/menu/NegoStep.tsx | 6 +-- .../chat/components/popup/GuideContent.tsx | 48 +++++++++---------- .../chat/components/templates/RejectCM.tsx | 2 +- .../chat/components/templates/RejectRSP.tsx | 2 +- .../features/chat/components/userInputs.tsx | 4 +- 10 files changed, 84 insertions(+), 57 deletions(-) create mode 100644 frontend/src/features/chat/components/MobileStepBar.tsx diff --git a/frontend/src/features/chat/components/ChatMessage.tsx b/frontend/src/features/chat/components/ChatMessage.tsx index a8d5c8d..281c44d 100644 --- a/frontend/src/features/chat/components/ChatMessage.tsx +++ b/frontend/src/features/chat/components/ChatMessage.tsx @@ -36,7 +36,7 @@ function ChatList() { if (!chats || chats.length === 0) { return (
-

채팅 내역이 없습니다.

+

채팅 내역이 없습니다.

) } diff --git a/frontend/src/features/chat/components/ChatSection.tsx b/frontend/src/features/chat/components/ChatSection.tsx index bd47e77..cbad604 100644 --- a/frontend/src/features/chat/components/ChatSection.tsx +++ b/frontend/src/features/chat/components/ChatSection.tsx @@ -1,11 +1,13 @@ import { useChatStore } from '@/features/chat/stores/useChatStore' import { ChatMessage } from '@/features/chat/components/ChatMessage' import { UserButton } from '@/features/chat/components/UserButton' +import { MobileStepBar } from '@/features/chat/components/MobileStepBar' export function ChatSection() { const { userButtonConfig } = useChatStore() return (
+ {/* 하단 액션 덱 */}
diff --git a/frontend/src/features/chat/components/MobileStepBar.tsx b/frontend/src/features/chat/components/MobileStepBar.tsx new file mode 100644 index 0000000..cbb37ea --- /dev/null +++ b/frontend/src/features/chat/components/MobileStepBar.tsx @@ -0,0 +1,46 @@ +import { Check } from 'lucide-react' +import { cn } from '@/lib' +import { useChatStore } from '@/features/chat/stores/useChatStore' +import { STEPS } from '@/features/chat/components/menu/NegoStep' + +// 모바일 전용: 협상절차를 채팅 상단에 가로 스텝바로 항상 표시한다. (데스크톱은 우측 패널 사용) +export function MobileStepBar() { + const chats = useChatStore((s) => s.messages) + const currentStep = chats[chats.length - 1]?.display_step || STEPS[0].name + const idx = Math.max( + 0, + STEPS.findIndex((s) => s.name === currentStep), + ) + + return ( +
+
+ {STEPS.map((step, i) => { + const done = i < idx + const current = i === idx + return ( +
+
+ {done ? : i + 1} +
+ {i < STEPS.length - 1 && ( +
+ )} +
+ ) + })} +
+

+ {idx + 1}. {STEPS[idx].name} + {STEPS[idx].desc} +

+
+ ) +} diff --git a/frontend/src/features/chat/components/RemainingTime.tsx b/frontend/src/features/chat/components/RemainingTime.tsx index c631117..b507076 100644 --- a/frontend/src/features/chat/components/RemainingTime.tsx +++ b/frontend/src/features/chat/components/RemainingTime.tsx @@ -20,7 +20,7 @@ export function RemainingTime() { return ( diff --git a/frontend/src/features/chat/components/menu/Contact.tsx b/frontend/src/features/chat/components/menu/Contact.tsx index 4a56ba1..7b2f38a 100644 --- a/frontend/src/features/chat/components/menu/Contact.tsx +++ b/frontend/src/features/chat/components/menu/Contact.tsx @@ -1,33 +1,14 @@ -import { useState } from 'react' -import { interactive, cn } from '@/lib' -import { ServiceGuidePopup } from '@/features/chat/components/popup/ServiceGuidePopup' - -// 헬프데스크 — 이용 가이드 버튼 클릭 시 안내 팝업을 연다. +// 헬프데스크 — 연락처. (안내 팝업 진입은 상단 '유의사항 및 이용방법' 섹션으로 일원화) export function Contact() { - const [isOpen, setIsOpen] = useState(false) - return (
헬프데스크
-
- -
- 010-0000-0000 - o2odev@o2o.kr -
+
+ 010-0000-0000 + o2odev@o2o.kr
- {isOpen && setIsOpen(false)} />}
) } diff --git a/frontend/src/features/chat/components/menu/NegoStep.tsx b/frontend/src/features/chat/components/menu/NegoStep.tsx index c813955..26f626f 100644 --- a/frontend/src/features/chat/components/menu/NegoStep.tsx +++ b/frontend/src/features/chat/components/menu/NegoStep.tsx @@ -7,7 +7,7 @@ interface Props { setIsOpen: (isOpen: boolean) => void } -const STEPS = [ +export const STEPS = [ { name: '서비스안내', desc: '협상 방식과 유의사항을 확인합니다.' }, { name: '담당자확인', desc: '협상 담당자 본인 여부를 확인합니다.' }, { name: '협상품목안내', desc: '대상 품목과 기준 단가를 확인합니다.' }, @@ -61,7 +61,7 @@ export function NegoStep({ isOpen, setIsOpen }: Props) {

{step.name}

-

+

{step.desc}

diff --git a/frontend/src/features/chat/components/popup/GuideContent.tsx b/frontend/src/features/chat/components/popup/GuideContent.tsx index 79ad893..4982d00 100644 --- a/frontend/src/features/chat/components/popup/GuideContent.tsx +++ b/frontend/src/features/chat/components/popup/GuideContent.tsx @@ -3,14 +3,13 @@ import { useChatInitStore } from '@/features/chat/stores/useChatInitStore' // 협상 유의사항 및 서비스 이용 방법 안내 — 팝업 2종(자동 안내/메뉴 가이드)이 공유하는 본문. // VAT/배송비 문구는 채팅 init 메타(useChatInitStore)를 읽어 상품별로 동적 표시한다. -// break-keep: 한국어 단어가 중간에서 잘려 줄바꿈되지 않도록 한다. -const textStyle = 'text-[17px] font-normal leading-[150%] tracking-[-0.34px] text-neutral-90 break-keep' +const bodyStyle = 'text-sm font-normal leading-relaxed text-neutral-70 break-keep' function Bullet({ children }: { children: ReactNode }) { return ( -
-

•

-
{children}
+
+ +
{children}
) } @@ -23,24 +22,26 @@ export function GuideContent() { return (
-
+
협상 유의 사항 및 서비스 이용 방법 안내
-
-
- 협상과 관련하여 다음 사항을 참고하여 견적에 참여해 주시기 바랍니다. -
+
+ 협상과 관련하여 다음 사항을 참고하여 견적에 참여해 주시기 바랍니다. +
+
협상 개시는  - Negosium 시스템의 협상 참여 버튼을 클릭하는 순간부터 시작 + + Negosium 시스템의 협상 참여 버튼을 클릭하는 순간부터 시작 + 됩니다. 부여된 협상 시간에  - + 응찰하지 않는 경우, 협상 참여의사가 없는 것으로 간주하여 재견적으로 진행 될 수 있습니다. @@ -48,25 +49,23 @@ export function GuideContent() { 협상에 입력되는 모든 가격은  - + {vat} 및 {deliveryFee}  기준이며,  - + 할인을 요청하는 경우 기존 공급가격에 할인율이 적용된 가격으로 환산 되어 제시됩니다. - 본 협상 결과에 대해서는 협상자와 협상대상자 간의 비밀 유지 조건으로 진행되고, 협상에서 - 얻어진 결과나 내용에 대해서는 당사자를 제외하고 제 3자에 공유할 수 없으며, 비밀 유지를 - 전제로 진행됩니다. + 본 협상 결과에 대해서는 협상자와 협상대상자 간의 비밀 유지 조건으로 진행되고, 협상에서 얻어진 결과나 내용에 + 대해서는 당사자를 제외하고 제 3자에 공유할 수 없으며, 비밀 유지를 전제로 진행됩니다. - 협상이 종결되면 특별한 사유 없이 취소 변경이 불가하니, 신중하게 협상에 참여해 주시기 - 바랍니다. + 협상이 종결되면 특별한 사유 없이 취소 변경이 불가하니, 신중하게 협상에 참여해 주시기 바랍니다. 안내된 사항 외 부분은 기존 견적 프로세스와 동일한 부분 유의 바랍니다. @@ -78,13 +77,12 @@ export function GuideContent() { // 하단 문의 안내 박스 export function GuideContactBox() { return ( -
-
- 기타 협상과정에서 궁금한 사항이나 문의하실 사항은 아래의 연락처로 상담을 부탁드립니다. +
+
+ 기타 협상 과정에서 궁금하거나 문의하실 사항은 아래 연락처로 상담 부탁드립니다.
- {/* textStyle 의 font-normal 과 같은 요소에서 충돌하지 않도록 bold 는 별도 조합으로 준다 */} -
- 헬프데스크 010-0000-0000, 이메일: o2odev@o2o.kr +
+ 헬프데스크 010-0000-0000 · o2odev@o2o.kr
) diff --git a/frontend/src/features/chat/components/templates/RejectCM.tsx b/frontend/src/features/chat/components/templates/RejectCM.tsx index f9d9b41..ded0edf 100644 --- a/frontend/src/features/chat/components/templates/RejectCM.tsx +++ b/frontend/src/features/chat/components/templates/RejectCM.tsx @@ -53,7 +53,7 @@ export function RejectCM() { return (
-
+
최종 공급 희망 가격 입력
diff --git a/frontend/src/features/chat/components/templates/RejectRSP.tsx b/frontend/src/features/chat/components/templates/RejectRSP.tsx index b5eadfe..05b6118 100644 --- a/frontend/src/features/chat/components/templates/RejectRSP.tsx +++ b/frontend/src/features/chat/components/templates/RejectRSP.tsx @@ -82,7 +82,7 @@ export function RejectRSP() { return (
-
+
협상 합의 불가 사유 및 최종 제안 단가 입력
diff --git a/frontend/src/features/chat/components/userInputs.tsx b/frontend/src/features/chat/components/userInputs.tsx index 793d6f8..ab20e89 100644 --- a/frontend/src/features/chat/components/userInputs.tsx +++ b/frontend/src/features/chat/components/userInputs.tsx @@ -32,13 +32,13 @@ function InputWithUnit({ value={value} onChange={onChange} onKeyDown={(e) => e.key === 'Enter' && onSubmit()} - className="h-[52px] flex-1 bg-neutral-10 rounded-xl outline-none border-2 border-brand-600 text-lg font-bold text-neutral-90 placeholder:text-sm placeholder:font-normal placeholder:text-neutral-50 pl-4 pr-[124px]" + className="h-[52px] flex-1 bg-neutral-10 rounded-xl outline-none border-2 border-brand-600 text-base font-bold text-neutral-90 placeholder:text-sm placeholder:font-normal placeholder:text-neutral-50 pl-4 pr-[124px]" placeholder={placeholder} aria-label={ariaLabel} aria-invalid={!!error} aria-describedby={error ? errorId : undefined} /> -
+
{value} {value && {unit}}