Merge branch 'feature/negodata'

This commit is contained in:
Mina Choi 2026-06-18 18:45:40 +09:00
commit ab84710888
5 changed files with 22 additions and 7 deletions

View File

@ -230,3 +230,4 @@ class chats(MainTableMixin, MAIN_BASE):
card_used_yn = Column(Boolean, nullable=True) # 카드 사용 여부
indicator_value = Column(Numeric(8, 6), nullable=True)
card_type = Column(SmallInteger, nullable=True) # 1=nego_card, 2=wild_card
meta = Column(JSONB, nullable=True) # 말풍선 표현 데이터(script/step/client_step/input_mode/input_options/chat_end)

View File

@ -29,6 +29,7 @@ import {
normalizeQuotationStatus,
buildBidSummary,
mapServerSessionView,
sessionStatusLabel,
mapServerCardView,
} from '../types';
@ -459,7 +460,7 @@ export function QuotationDetailDrawer({
<TableCell className="p-3 text-center">
<span
className={`inline-flex px-2 py-0.5 rounded-full text-[10px] font-bold ${
sess.status === '협상종료' || sess.status === 'COMPLETED'
sess.status === '협상완료' || sess.status === 'COMPLETED'
? 'bg-blue-100 text-blue-800 dark:bg-blue-950/20 dark:text-blue-300'
: sess.status === '협상거부' || sess.status === 'REJECTED'
? 'bg-red-100 text-red-800 dark:bg-red-950/20 dark:text-red-300'
@ -561,7 +562,7 @@ export function QuotationDetailDrawer({
{serverSessions.map((sd) => {
const isSelected = sd.session_id === effectiveSessionId;
const name = partners.find((p) => p.id === sd.supplier_id)?.name || sd.supplier_id;
const statusLabel = sd.status === 2 ? '협상종료' : sd.status === 3 ? '협상거부' : '협상중';
const statusLabel = sessionStatusLabel(sd.status);
return (
<button
key={sd.session_id}
@ -576,7 +577,7 @@ export function QuotationDetailDrawer({
className={`text-[9px] font-bold px-1.5 py-0.5 rounded ${
statusLabel === '협상거부'
? 'bg-rose-100 text-rose-800 dark:bg-rose-950/30 dark:text-rose-300'
: statusLabel === '협상종료'
: statusLabel === '협상완료'
? 'bg-blue-100 text-blue-800 dark:bg-blue-950/30 dark:text-blue-300'
: 'bg-emerald-100 text-emerald-800 dark:bg-emerald-950/30 dark:text-emerald-300'
}`}

View File

@ -172,9 +172,11 @@ export function useQuotations() {
try {
const res = await createQuotationMutation.mutateAsync({ data: payload });
const qtId = res?.quotation?.qt_id ?? null;
// 서버가 result.success=false 거나 qt_id 가 없으면 실패로 처리(HTTP 200 이어도). 모달은 그대로 유지.
// 에러는 응답 패킷 규약(result.success/code/desc + msg)대로 표시한다. HTTP 200 이어도 success=false면 실패.
if (!res?.result?.success || !qtId) {
showToast(`견적 생성 실패: ${res?.result?.desc ?? '서버 오류'}`, 'error');
const reason = res?.msg ?? res?.result?.desc ?? '서버 오류';
const code = res?.result?.code;
showToast(`견적 생성 실패${code ? ` [${code}]` : ''}: ${reason}`, 'error');
return null;
}
// 목록 재조회가 끝나야 새 견적이 정본 목록에 들어온다(상세 자동오픈이 그 행을 찾을 수 있게).

View File

@ -259,7 +259,18 @@ export function buildSessions(
// ── 서버 연동 매퍼(negotiation.sessions / chats / 사용 카드) ──────────────
const SESSION_STATUS_LABEL: Record<number, string> = { 1: '협상중', 2: '협상종료', 3: '협상거부' };
// 세션상태 코드→라벨. 서버 /v1/enums(session_status) · SHARED_ENUMS.md 5-state 와 동일해야 한다.
// (정본은 서버 enum — 여기 값은 그걸 미러링한 것이며 드리프트 시 서버 기준으로 맞춘다.)
export const SESSION_STATUS_LABEL: Record<number, string> = {
1: '협상생성',
2: '협상중',
3: '협상완료',
4: '미참여',
5: '협상거부',
};
// 코드→라벨 단일 진입점. 미정의 코드는 코드 문자열 그대로.
export const sessionStatusLabel = (code?: number | null): string =>
(code != null ? SESSION_STATUS_LABEL[code] : undefined) ?? String(code ?? '');
const DELIVERY_TYPE_LABEL: Record<number, string> = { 1: '협력사배송', 2: '지정택배배송', 3: '픽업배송' };
// ISO 문자열 → 'YYYY-MM-DD HH:mm'. 빈 값/파싱 실패는 '-'.

View File

@ -285,7 +285,7 @@ export interface ChatMessage {
export interface ChatSession {
id: string; // matches supplier_id (or supplier.supplier_id)
partnerName: string;
status: 'NEGOTIATING' | 'COMPLETED' | 'REJECTED' | '협상생성' | '협상중' | '협상종료' | '미참여' | '협상거부';
status: 'NEGOTIATING' | 'COMPLETED' | 'REJECTED' | '협상생성' | '협상중' | '협상완료' | '미참여' | '협상거부';
currentBid: number;
bidTime: string;
messages: ChatMessage[];