/** * 몰별 확인 상태의 표기 — **운영자용**(상세). 정의는 lps/docs/result-states.md. * * 실사용자 화면(negodata)은 이걸 3가지로 접어서 보여준다. 여기서 접지 않는 이유는 * 운영자의 목적이 **진단**이기 때문이다: `blocked`(자동 회복)와 `env_blocked`(사람이 고쳐야 함)를 * 뭉뚱그리면 회복될 일에 매달리거나 손봐야 할 설정을 방치하게 된다. * * 색은 전부 index.css @theme 토큰을 참조한다(raw hex 금지 — 토큰을 바꾸면 여기도 함께 바뀌어야 함). */ import type { SourceState } from "../api/types"; type Meta = { label: string; color: string; /** 한 줄 설명 — '이게 무슨 뜻이고 내가 뭘 해야 하나' */ desc: string; /** 그 몰을 실제로 확인했는가. false 면 '없다'고 말하면 안 된다 */ confirmed: boolean; }; export const SOURCE_STATES: Record = { matched: { label: "매칭", color: "var(--color-ok-600)", confirmed: true, desc: "수집·매칭 성공 — 가격 확보", }, no_match: { label: "같은 상품 없음", color: "var(--color-neutral-400)", confirmed: true, desc: "수집은 됐으나 같은 상품이 아님 — 검색어·규격을 의심할 것", }, empty: { label: "결과 0건", color: "var(--color-neutral-400)", confirmed: true, desc: "그 몰의 검색 결과 자체가 0건 — 확인했고 정말 없다", }, blocked: { label: "차단", color: "var(--color-warn-600)", confirmed: false, desc: "안티봇 차단 — IP 회전으로 자동 회복된다. 반복되면 IP 풀·예산 확인", }, env_blocked: { label: "환경 차단", color: "var(--color-dead-600)", confirmed: false, desc: "회전해도 회복 불가 — 사람이 환경/게이트웨이 설정을 고쳐야 한다", }, unavailable: { label: "확인 못함", color: "var(--color-warn-600)", confirmed: false, desc: "전송 실패·가용 IP 없음 등 일시적 — 잠시 후 재시도로 회복", }, skipped: { label: "미사용", color: "var(--color-neutral-300)", confirmed: true, desc: "그 소스를 쓰지 않음(폴백 OFF 등)", }, }; /** 미지의 상태도 화면을 깨뜨리지 않는다 — 값 그대로 보여주고 '모름'으로 취급한다. */ export const stateMeta = (s: string | undefined): Meta => SOURCE_STATES[s as SourceState] ?? { label: s || "-", color: "var(--color-ink-400)", desc: "알 수 없는 상태", confirmed: false, }; /** 못 본 몰 목록 — 결과가 왜 최종이 아닌지 한 줄로 설명할 때 쓴다. */ export const unconfirmedMalls = (sources?: Record): string[] => Object.entries(sources ?? {}) .filter(([, v]) => !stateMeta(v?.state).confirmed) .map(([mall]) => mall);