diff --git a/lps-admin/src/index.css b/lps-admin/src/index.css index 41556b9..fbdc5e1 100644 --- a/lps-admin/src/index.css +++ b/lps-admin/src/index.css @@ -34,6 +34,10 @@ --color-dead-600: #d03b3b; --color-dead-50: #fdeaea; + /* 중립 계열(차트 보조 — 유휴/종료 등 '무해한 상태') */ + --color-neutral-400: #898781; + --color-neutral-300: #c3c2b7; + /* 차트 시리즈(엔터티 고정) */ --color-naver: #008a43; --color-coupang: #f0455b; diff --git a/lps-admin/src/pages/Crawl.tsx b/lps-admin/src/pages/Crawl.tsx index 5558084..99ca707 100644 --- a/lps-admin/src/pages/Crawl.tsx +++ b/lps-admin/src/pages/Crawl.tsx @@ -10,15 +10,16 @@ import type { BotStatsRes, IpSessionStatsRes } from "../api/types"; import { Card, Empty, ErrorNote, Loading } from "../components/ui"; import { dateShort, durationSec, hhmm } from "../lib/format"; -// 종료 사유 — 의미 기반 상태색(정상 회전=초록 계열, 차단=적색, 포트사망=주황, 중립=회색/파랑). +// 종료 사유 — 의미 기반 상태색. 색은 전부 index.css @theme 토큰 참조(단일 소스) — +// raw hex 재정의 금지(토큰을 바꾸면 여기도 함께 바뀌어야 한다). const REASONS: Record = { - budget: { label: "예산 선제 회전", color: "#0ca30c", desc: "정상 — 차단 전에 IP 교체(평판 보존)" }, - window: { label: "시간창 만료", color: "#2a78d6", desc: "정상 — sticky 10분 주기 교체" }, - idle: { label: "유휴 정리", color: "#898781", desc: "정상 — 한동안 검색 없어 브라우저 회수" }, - shutdown: { label: "종료", color: "#c3c2b7", desc: "정상 — 워커 재시작/종료" }, - rotate: { label: "기타 회전", color: "#9aa1ac", desc: "웜업 재시도 등" }, - proxy_error: { label: "포트 사망", color: "#ec835a", desc: "주의 — 프록시 전송 실패로 교체" }, - block: { label: "차단됨", color: "#d03b3b", desc: "위험 — 예산 안에서도 차단(예산 하향 검토)" }, + budget: { label: "예산 선제 회전", color: "var(--color-ok-600)", desc: "정상 — 차단 전에 IP 교체(평판 보존)" }, + window: { label: "시간창 만료", color: "var(--color-run-600)", desc: "정상 — sticky 10분 주기 교체" }, + idle: { label: "유휴 정리", color: "var(--color-neutral-400)", desc: "정상 — 한동안 검색 없어 브라우저 회수" }, + shutdown: { label: "종료", color: "var(--color-neutral-300)", desc: "정상 — 워커 재시작/종료" }, + rotate: { label: "기타 회전", color: "var(--color-ink-400)", desc: "웜업 재시도 등" }, + proxy_error: { label: "포트 사망", color: "var(--color-warn-600)", desc: "주의 — 프록시 전송 실패로 교체" }, + block: { label: "차단됨", color: "var(--color-dead-600)", desc: "위험 — 예산 안에서도 차단(예산 하향 검토)" }, }; const RANGES = [ @@ -41,7 +42,7 @@ export default function Crawl() { }); const reasons = Object.entries(ip.data?.by_reason ?? {}) - .map(([k, v]) => ({ key: k, ...(REASONS[k] ?? { label: k, color: "#9aa1ac", desc: "" }), value: v })) + .map(([k, v]) => ({ key: k, ...(REASONS[k] ?? { label: k, color: "var(--color-ink-400)", desc: "" }), value: v })) .sort((a, b) => b.value - a.value); const totalSessions = reasons.reduce((s, r) => s + r.value, 0); const blockCount = ip.data?.by_reason?.block ?? 0; @@ -122,8 +123,8 @@ export default function Crawl() { [`${v}건`, "세션 수"]} labelFormatter={(l) => `IP당 ${l}회 요청`} /> {ip.data.block_min_requests != null && ( - + )} @@ -147,7 +148,7 @@ export default function Crawl() { [`${v}건`, "차단"]} /> - + )} @@ -202,7 +203,7 @@ export default function Crawl() { {ip.data.sessions.map((s, i) => { - const r = REASONS[s.end_reason] ?? { label: s.end_reason, color: "#9aa1ac" }; + const r = REASONS[s.end_reason] ?? { label: s.end_reason, color: "var(--color-ink-400)" }; return ( {dateShort(s.created_at)}