diff --git a/apps/web/public/app.js b/apps/web/public/app.js index 37917d4..bc0a71a 100644 --- a/apps/web/public/app.js +++ b/apps/web/public/app.js @@ -436,7 +436,7 @@ function render() { elements.totalCount.textContent = String(counts.total); elements.activeCount.textContent = String(counts.active); elements.staleCount.textContent = String(counts.stale); - elements.lastUpdated.textContent = locations[0] ? `${locations.length}대 표시 · ${formatDateTime(locations[0].receivedAt)}` : "수신 대기"; + elements.lastUpdated.textContent = locations[0] ? `${locations.length}대 · ${formatTime(locations[0].receivedAt)}` : "수신 대기"; renderOperationsSummary(locations, counts); renderMapStatus(locations, counts); syncStatusFilterButtons(); @@ -448,7 +448,7 @@ function render() { if (!visibleLocations.length) { const empty = document.createElement("div"); empty.className = "empty-state"; - empty.textContent = locations.length ? "조건에 맞는 차량이 없습니다." : "수신된 차량 위치가 없습니다."; + empty.textContent = locations.length ? "조건에 맞는 차량이 없습니다." : "수신 위치가 없습니다."; elements.vehicleList.append(empty); return; } @@ -459,16 +459,16 @@ function render() { } function renderOperationsSummary(locations, counts) { - const activeText = counts.active > 0 ? `${counts.active}대 운행중` : "운행 대기"; - const staleText = counts.stale > 0 ? `주의 ${counts.stale}대 확인 필요` : "모든 차량 정상 수신"; + const activeText = counts.active > 0 ? `${counts.active}대 운행 중` : "운행 대기"; + const staleText = counts.stale > 0 ? `주의 ${counts.stale}대` : "정상 수신"; const averageEtaMinutes = calculateAverageEtaMinutes(locations); const cargoTypes = new Set(locations.map(getCargoName).filter(Boolean)); const routeCount = locations.filter((location) => getRoutePath(location).length >= 2).length; elements.opsHeadline.textContent = `전국 ${activeText}`; elements.opsSubline.textContent = locations.length - ? `${staleText} · ${locations.length}대 실시간 관제` - : "시뮬레이터 또는 기사 앱의 첫 위치를 기다리고 있습니다."; + ? `${staleText} · ${locations.length}대 관제 중` + : "첫 위치를 기다리는 중입니다."; elements.averageEta.textContent = averageEtaMinutes === null ? "-" : formatDuration(averageEtaMinutes).replace("약 ", ""); elements.cargoTypeCount.textContent = cargoTypes.size ? `${cargoTypes.size}종` : "-"; elements.routePreviewCount.textContent = routeCount ? `${routeCount}건` : "-"; @@ -595,7 +595,7 @@ function createVehicleItem(location) { const speedUnit = document.createElement("span"); speedUnit.textContent = "km/h"; const etaValue = document.createElement("small"); - etaValue.textContent = getRouteProgressSummary(location) || "ETA 대기"; + etaValue.textContent = getRouteProgressSummary(location) || "도착 대기"; speed.append(speedValue, speedUnit, etaValue); main.append(title, meta, routeLine, cargoLine, progress); @@ -1110,18 +1110,18 @@ function createDestinationMarkerHtml(location) { function renderRouteFocusPanel(location) { if (!location) { elements.routeFocusPanel.classList.remove("is-active"); - elements.routeFocusTitle.textContent = "차량을 선택하세요"; - elements.routeFocusMeta.textContent = "목적지 경로와 예상 도착 시간이 지도 위에 표시됩니다."; + elements.routeFocusTitle.textContent = "차량 선택"; + elements.routeFocusMeta.textContent = "선택하면 경로와 도착 예정 시간이 표시됩니다."; return; } const metrics = getRouteMetrics(location); const vehicleType = getVehicleType(location); const cargo = getCargoSummary(location); - const destination = getDestinationName(location) || "목적지 미등록"; const progress = metrics - ? `${formatDistance(metrics.remainingDistanceKm)} 남음 · ${formatDuration(metrics.etaMinutes)} · ${formatEtaTime(metrics.estimatedArrivalAt)} 도착` + ? `${formatDistance(metrics.remainingDistanceKm)} 남음 · ${formatClockTime(metrics.estimatedArrivalAt)} 도착` : "경로 데이터 대기"; + const destination = getDestinationName(location) || "목적지 미등록"; elements.routeFocusPanel.classList.add("is-active"); elements.routeFocusTitle.textContent = `${location.vehicleNo || location.driverId} · ${vehicleType.label}`; @@ -1351,8 +1351,8 @@ function getLocationStatus(location) { const receivedAt = new Date(location.receivedAt).getTime(); const ageMs = Date.now() - receivedAt; - if (ageMs <= 2 * 60 * 1000) return { key: "active", label: "수신중" }; - if (ageMs <= 10 * 60 * 1000) return { key: "stale", label: "지연" }; + if (ageMs <= 2 * 60 * 1000) return { key: "active", label: "수신 중" }; + if (ageMs <= 10 * 60 * 1000) return { key: "stale", label: "주의" }; return { key: "offline", label: "미수신" }; } @@ -1363,8 +1363,7 @@ function formatDateTime(value) { month: "2-digit", day: "2-digit", hour: "2-digit", - minute: "2-digit", - second: "2-digit" + minute: "2-digit" }).format(date); } @@ -1411,16 +1410,16 @@ function getRouteSummary(location) { const origin = firstString(location.origin, location.departure); const destination = firstString(location.destination, location.arrival); - if (origin && destination) return `구간: ${origin} -> ${destination}`; - if (destination) return `도착지: ${destination}`; - if (origin) return `상차지: ${origin}`; - return "도착지: 미등록"; + if (origin && destination) return `${origin} → ${destination}`; + if (destination) return `도착: ${destination}`; + if (origin) return `상차: ${origin}`; + return "도착 미등록"; } function getRouteProgressSummary(location) { const metrics = getRouteMetrics(location); if (!metrics) return ""; - return `남은 ${formatDistance(metrics.remainingDistanceKm)} · ETA ${formatEtaTime(metrics.estimatedArrivalAt)}`; + return `${formatDistance(metrics.remainingDistanceKm)} · ${formatClockTime(metrics.estimatedArrivalAt)} 도착`; } function getRouteProgressPercent(location) { @@ -1614,6 +1613,16 @@ function formatEtaTime(value) { }).format(date); } +function formatClockTime(value) { + const date = value instanceof Date ? value : new Date(value); + if (Number.isNaN(date.getTime())) return "-"; + return new Intl.DateTimeFormat("ko-KR", { + hour: "2-digit", + minute: "2-digit", + hour12: false + }).format(date); +} + function firstString(...values) { for (const value of values) { if (value === undefined || value === null) continue; diff --git a/apps/web/public/index.html b/apps/web/public/index.html index bbff03a..54c63d9 100644 --- a/apps/web/public/index.html +++ b/apps/web/public/index.html @@ -3,7 +3,7 @@
-목적지 경로와 예상 도착 시간이 지도 위에 표시됩니다.
+ 선택 경로 + 차량 선택 +선택하면 경로와 도착 예정 시간이 표시됩니다.