diff --git a/src/features/crm/EfficiencyScatter.jsx b/src/features/crm/EfficiencyScatter.jsx
index 2ac947f..d8d2d21 100644
--- a/src/features/crm/EfficiencyScatter.jsx
+++ b/src/features/crm/EfficiencyScatter.jsx
@@ -1,9 +1,9 @@
import { useMemo, useState } from 'react'
-// 전체 드라이버 효율 산점도 (모던 리디자인).
+// 전체 UCO 드라이버 산점도.
// x = 이동거리(km), y = 오일 픽업량
-// 마커 = 번호 배지 + 진행률 링(완료/목표). 운행중=채운 배지 / 종료=옅은 배지+체크 / 픽업전=고스트
-// 호버: 해당 마커가 맨 위로 + 상세 툴팁(시작/종료 시각 포함). 클릭: onSelect(driverId)
+// 잔 채움 = 픽업량 / 목표량(고정). 종료=체크 배지 / 일시정지=막대 배지 / 픽업 0=고스트
+// 호버: 해당 마커가 맨 위로 + 상세 툴팁. 클릭: onSelect(driverId)
const W = 720, H = 470, PL = 56, PR = 680, PT = 44, PB = 400, R = 11
const ACCENT = '#2e7d32'
@@ -17,6 +17,7 @@ const fmtHM = (s) => {
if (isNaN(d)) return '—'
return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`
}
+const statusText = (s) => (s === 'C' ? '운행 종료' : s === 'P' ? '일시정지' : '운행 중')
// 단색(BI) 툴팁 아이콘. y = 텍스트 baseline 기준, 약 13px.
const TipIcon = ({ type, x, y, c }) => {
@@ -69,7 +70,7 @@ export default function EfficiencyScatter({ rows, onSelect, selectedId }) {
targetQty: Number(r.ddsTargetQty ?? 0), // 목표량 (고정) ← 분모
completed: Number(r.ddsCompletedCount ?? 0),
targetCount: Number(r.ddsTargetCount ?? 0), // 목표 방문수 (고정)
- running: r.ddsStatus !== 'C', // C = 운행종료
+ status: r.ddsStatus,
startAt: r.ddsStartAt,
endAt: r.ddsEndAt,
}))
@@ -181,7 +182,7 @@ export default function EfficiencyScatter({ rows, onSelect, selectedId }) {
const { items, xTicks, yTicks, yOf, xOf } = model
if (!items.length) {
- return
픽업이 기록된 드라이버가 없습니다.
+ return 목표가 배정된 드라이버가 없습니다.
}
// 호버된 마커를 맨 마지막에 그려서 위로 올림 (행 단위 식별)
@@ -197,8 +198,10 @@ export default function EfficiencyScatter({ rows, onSelect, selectedId }) {
const k = isHover ? 1.18 : 1
const r = R * k
const cx = d.mx, cy = d.my
- const empty = d.oil <= 0 // 픽업 없음 (산점도엔 거의 안 옴, 안전장치)
- const f = d.targetQty > 0 ? Math.min(1, d.oil / d.targetQty) : 1 // 픽업량 / 목표량 (예상 없으면 가득)
+ const empty = d.oil <= 0 // 픽업 0 (목표만 있는 드라이버 등)
+ const ended = d.status === 'C'
+ const paused = d.status === 'P'
+ const f = d.targetQty > 0 ? Math.min(1, d.oil / d.targetQty) : 1 // 픽업량 / 목표량(고정)
const clipId = `dstatLiq${d.rowId}`
// 액체 표면 (가벼운 물결) + 바닥. f=0/1 이면 평평.
@@ -218,13 +221,13 @@ export default function EfficiencyScatter({ rows, onSelect, selectedId }) {
{isSel && }
{empty ? (
- // 픽업 전 / 예상량 없음 = 빈 점선 잔
+ // 픽업 0 / 목표만 있음 = 빈 점선 잔
<>
{d.num}
>
) : (
- // 액체 잔: 안쪽이 아래에서부터 차오름 (f = 픽업/예상)
+ // 액체 잔: 안쪽이 아래에서부터 차오름 (f = 픽업/목표)
<>
@@ -232,13 +235,23 @@ export default function EfficiencyScatter({ rows, onSelect, selectedId }) {
{d.num}
- {!d.running && (
- <>
-
-
- >
- )}
+ >
+ )}
+
+ {ended && (
+ // 운행 종료 체크
+ <>
+
+
+ >
+ )}
+ {paused && (
+ // 일시정지 (두 막대)
+ <>
+
+
+
>
)}
@@ -263,7 +276,7 @@ export default function EfficiencyScatter({ rows, onSelect, selectedId }) {
: `${Math.round(d.oil).toLocaleString()} L` },
{ ic: 'dist', t: d.kmText || `${d.km} km` },
{ ic: 'visit', t: d.targetCount > 0 ? `${d.completed} / ${d.targetCount}` : '방문 목표 없음' },
- { ic: 'time', t: d.endAt ? `${fmtHM(d.startAt)} ~ ${fmtHM(d.endAt)}` : `${fmtHM(d.startAt)} ~ 운행 중` },
+ { ic: 'time', t: d.endAt ? `${fmtHM(d.startAt)} ~ ${fmtHM(d.endAt)}` : `${fmtHM(d.startAt)} ~ ${statusText(d.status)}` },
]
const lineH = 16
const tw = 168, th = 24 + rows.length * lineH