관제 화면 문구와 반응형 표시 개선

This commit is contained in:
2026-06-06 19:47:51 +09:00
parent b2562e9145
commit e5ae236b8a
3 changed files with 71 additions and 37 deletions

View File

@@ -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;