From fab8e5aa53ca03204ea6b43f49892e48f35ac9bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=AF=BC=ED=97=8C?= Date: Thu, 18 Jun 2026 13:12:31 +0900 Subject: [PATCH] =?UTF-8?q?feat(frontend):=20=ED=98=91=EC=83=81=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D/=EC=B0=B8=EC=97=AC/=EA=B1=B0=EB=B6=80=20?= =?UTF-8?q?=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 세션 목록을 useSessionListQuery 로 서버 조회(필터·정렬·페이지 위임), mock 제거. 정수 코드↔한국어 라벨 변환 어댑터(list/lib/adapter) - 협상 참여: useParticipateMutation, 성공 시 채팅 진입·실패 토스트 - 거부: 사유 입력 팝업(RejectPopup) + useRejectMutation, 상태별 차단 안내 - 선택/상태 검증을 토스트로 일원화(ActionSection 은 버튼만 담당) Co-Authored-By: Claude Opus 4.8 (1M context) --- .../list/components/ActionSection.tsx | 39 +++++- .../features/list/components/RejectPopup.tsx | 124 ++++++++++++++++++ .../list/containers/ContentContainer.tsx | 85 +++++++++++- frontend/src/features/list/hooks/useList.ts | 45 +++---- frontend/src/features/list/lib/adapter.ts | 41 ++++++ frontend/src/features/list/mocks/mockItems.ts | 115 ---------------- 6 files changed, 298 insertions(+), 151 deletions(-) create mode 100644 frontend/src/features/list/components/RejectPopup.tsx create mode 100644 frontend/src/features/list/lib/adapter.ts delete mode 100644 frontend/src/features/list/mocks/mockItems.ts diff --git a/frontend/src/features/list/components/ActionSection.tsx b/frontend/src/features/list/components/ActionSection.tsx index 36ce83c..5a52efc 100644 --- a/frontend/src/features/list/components/ActionSection.tsx +++ b/frontend/src/features/list/components/ActionSection.tsx @@ -5,17 +5,42 @@ const PILL = 'text-lg font-semibold whitespace-nowrap ' + interactive -export function ActionSection() { +export interface ActionSectionProps { + isParticipating: boolean + isRejecting: boolean + onParticipate: () => void + onReject: () => void +} + +export function ActionSection({ + isParticipating, + isRejecting, + onParticipate, + onReject, +}: ActionSectionProps) { return (
- {/* TODO: 협상 참여 동작 연동 */} - - {/* TODO: 거부 동작 연동 */} + diff --git a/frontend/src/features/list/components/RejectPopup.tsx b/frontend/src/features/list/components/RejectPopup.tsx new file mode 100644 index 0000000..d7f2d45 --- /dev/null +++ b/frontend/src/features/list/components/RejectPopup.tsx @@ -0,0 +1,124 @@ +import { useState } from 'react' +import { Modal } from '@/components' +import { cn, interactive } from '@/lib' + +const REASONS = ['단종', '품절', '기타'] as const + +export interface RejectPopupProps { + onClose: () => void + /** 최종 거부 사유 (프리셋 라벨 또는 기타 입력 텍스트) */ + onSubmit: (reason: string) => void +} + +// 거부 사유 입력 팝업 (단종/품절/기타). +export function RejectPopup({ onClose, onSubmit }: RejectPopupProps) { + const [selectedReason, setSelectedReason] = useState(null) + const [customReason, setCustomReason] = useState('') + const [showError, setShowError] = useState(false) + + const isEtcOpen = selectedReason === '기타' + const isSubmitDisabled = !selectedReason || (isEtcOpen && !customReason.trim()) + + const handleReasonClick = (reason: string) => { + setShowError(false) + if (selectedReason === reason) { + setSelectedReason(null) + setCustomReason('') + } else { + setSelectedReason(reason) + if (reason !== '기타') setCustomReason('') + } + } + + const handleSubmit = () => { + if (isEtcOpen && !customReason.trim()) { + setShowError(true) + return + } + if (isSubmitDisabled || !selectedReason) return + + const reason = isEtcOpen ? customReason.trim() : selectedReason + onSubmit(reason) + onClose() + } + + return ( + +
+ {/* 헤더 */} +

+ 거부 사유를 입력해주세요 +

+ + {/* 사유 선택 */} +
+ {REASONS.map((reason) => ( + + ))} +
+ + {/* 기타 입력 + 에러 */} +
+
+