o2o-negosium-original/front/src/features/chat/components/templates/rejectControls.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

80 lines
2.1 KiB
TypeScript

import { Loader2 } from 'lucide-react'
import { cn } from '@/lib'
// reject 폼 공유 컨트롤 (RejectCM / RejectRSP 공용)
export function SelectRadio({
text,
value,
name,
selectedValue,
onChange,
errorMessage,
disabled,
}: {
text: string
value: string
name: string
selectedValue: string
onChange: (value: string) => void
errorMessage: string
disabled?: boolean
}) {
const isChecked = selectedValue === value
return (
<label className={cn('flex items-center gap-2', disabled ? 'cursor-not-allowed' : 'cursor-pointer')}>
<input
className={cn(
'appearance-none w-[16px] h-[16px] min-w-[16px] min-h-[16px] rounded-full border-[1.5px] whitespace-nowrap',
disabled ? 'cursor-not-allowed' : 'cursor-pointer',
isChecked
? 'border-neutral-70 bg-[radial-gradient(circle,var(--neutral-70)_40%,white_40%)]'
: errorMessage
? 'border-negative bg-white'
: 'border-neutral-60 bg-white',
)}
type="radio"
name={name}
value={value}
checked={isChecked}
onChange={(e) => onChange(e.target.value)}
disabled={disabled}
/>
<div className={cn('reject break-keep', disabled && 'text-neutral-60')}>{text}</div>
</label>
)
}
export function SubmitButton({
isValid,
isLoading,
isSubmitted,
onSubmit,
}: {
isValid: boolean
isLoading: boolean
isSubmitted: boolean
onSubmit: () => void
}) {
if (isSubmitted) return null
const style = isLoading
? 'bg-neutral-40 text-neutral-80 cursor-not-allowed'
: isValid
? 'bg-neutral-90 text-white cursor-pointer hover:bg-neutral-80'
: 'bg-neutral-20 text-neutral-60 cursor-not-allowed'
return (
<button
className={cn(
'flex items-center justify-center w-[280px] h-[48px] rounded-[12px] text-base font-bold tracking-[-0.32px] transition-all duration-200',
style,
)}
onClick={onSubmit}
disabled={!isValid || isLoading}
>
{isLoading ? <Loader2 className="size-5 animate-spin" /> : '제출'}
</button>
)
}