import type { ReactNode } from 'react'; import { BadgeCheck, TrendingDown, TrendingUp } from 'lucide-react'; import { Card } from '@/components/ui/card'; import { Typography } from '@/components/ui/typography'; import { cn } from '@/lib/utils'; import type { QuotationData } from '@/api/generated/model/quotationData'; import { StatusPill, type PillTone } from './StatusPill'; import { PricingSpectrum } from './PricingSpectrum'; import { buildQuotationResult, CHAIN_ROUND_STATE_LABEL, type ChainRoundState, type SessionView, } from '../../types'; const won = (n?: number | null) => (n != null ? `₩${n.toLocaleString()}` : '-'); // 결과 상태별 배지 톤(협상현황 pill 팔레트 재사용). const OUTCOME_TONE: Record = { awarded: 'emerald', regenerated: 'amber', failed: 'blue', active: 'zinc', }; /** 견적 결과 요약 밴드 — 마감 사유/낙찰 협력사 + 절감, 하단에 단가 조정 흐름 스펙트럼(최고·최저 투찰가/목표가). */ export function ResultSummaryBand({ quotation, sessionViews, }: { quotation: QuotationData; sessionViews: SessionView[]; }) { const r = buildQuotationResult(quotation, sessionViews); const good = r.savings != null && r.savings >= 0; const pct = r.savingsRate != null ? `${Math.abs(r.savingsRate * 100).toFixed(1)}%` : null; const SavingsIcon = good ? TrendingDown : TrendingUp; return ( {/* 결과/사유 + 낙찰 협력사 + 절감 */}
{CHAIN_ROUND_STATE_LABEL[r.outcome]} {/* 낙찰 건은 협력사명을 주줄로(사유 '낙찰'은 pill 과 중복이라 생략), 그 외엔 마감사유를 표기. */} {r.winnerName ? (
낙찰 협력사 {r.winnerName}
) : ( {r.closeReason} )}
{r.savings == null ? ( - ) : ( {good ? '' : '−'} {won(Math.abs(r.savings))} {pct ? ` (${pct})` : ''} )}
{/* 단가 조정 흐름 스펙트럼: 최고 투찰가 → 최저 투찰가 · 목표가 */}
); } /** 밴드 안의 라벨/값 한 칸. */ function Stat({ label, sub, children }: { label: string; sub?: string; children: ReactNode }) { return (
{label} {sub ? ` · ${sub}` : ''} {children}
); }