From f87b25bc50bf0841cf57bf19cdf760c27de952a4 Mon Sep 17 00:00:00 2001 From: seo Date: Fri, 26 Jun 2026 01:56:09 +0900 Subject: [PATCH] Compact car monitor durations by card width --- README.md | 4 ++-- monitor.php | 21 ++++++++++++++------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 040bd78..7fb1236 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Car는 차량 모뎀/게이트웨이에서 받은 상태를 수집해 DB에 저 - 허용된 차량 명령 TCP 전송 - monitor 화면과 AJAX 상태 갱신 - monitor 화면 WakeLock, 언어, 테마 토글 -- 모바일 폭에 맞춘 사용량 2줄 배치, 로그 가로 스크롤, 시간 문구 축약 +- 모바일 및 좁은 카드 폭에 맞춘 사용량 2줄 배치, 로그 가로 스크롤, 시간 문구 축약 - 사용량/요금 표시와 보정 metadata - 4.95초 UI timeout과 60초 사용량 정산 분리 - TCP 실패 reason과 마지막 수신 지연 표시 @@ -39,7 +39,7 @@ Car는 차량 모뎀/게이트웨이에서 받은 상태를 수집해 DB에 저 상태 기록 섹션의 경과는 각 로그가 기록된 시각부터 현재까지 흐른 시간입니다. 각 행의 `age_seconds`는 서버 현재시각과 로그 timestamp의 차이로 계산해 내려주며, 화면 갱신마다 상단 조회 주기 경과처럼 계속 증가합니다. -모바일처럼 폭이 좁은 화면에서는 조회 경과와 시동 유지 시간이 한 줄로 유지되도록 초, 필요 시 분 단위를 생략해 표시합니다. 사용량 지표는 3열 2줄로 카드 안에 배치하고, 상태 기록 테이블은 카드 내부 좌우 스크롤로 분리해 전체 페이지 레이아웃이 밀리지 않게 합니다. +모바일처럼 화면이나 상태 카드 폭이 좁을 때는 조회 경과와 시동 유지 시간이 한 줄로 유지되도록 초, 필요 시 분 단위를 생략해 표시합니다. 사용량 지표는 3열 2줄로 카드 안에 배치하고, 상태 기록 테이블은 카드 내부 좌우 스크롤로 분리해 전체 페이지 레이아웃이 밀리지 않게 합니다. 실시간 차량 그림의 도어/트렁크 열림 표시선은 열림 애니메이션 중에도 차량 기준 위치를 유지합니다. 트렁크 표시선은 중앙 정렬 기준을 유지한 상태에서 확대되어 PC와 모바일 모두에서 차체 하단과 맞도록 표시됩니다. diff --git a/monitor.php b/monitor.php index b23bba3..1ae87e7 100644 --- a/monitor.php +++ b/monitor.php @@ -1916,9 +1916,9 @@ if (isset($_GET['mode']) && $_GET['mode'] === 'usage') { freshnessEl.className = isReceiveBad || isStale ? 'font-mono small mt-1 text-warning' : 'font-mono small mt-1 text-muted'; const ageEl = document.getElementById('age-seconds'); - if (age > 60) ageEl.className = 'font-mono fw-bold text-danger'; - else if (age > 30) ageEl.className = 'font-mono fw-bold text-warning'; - else ageEl.className = 'font-mono fw-bold text-muted'; + if (age > 60) ageEl.className = 'font-mono fw-bold text-danger duration-line'; + else if (age > 30) ageEl.className = 'font-mono fw-bold text-warning duration-line'; + else ageEl.className = 'font-mono fw-bold text-muted duration-line'; const voltage = parseFloat(data.battery_voltage || 0); const batteryEl = document.getElementById('val-battery'); @@ -2143,15 +2143,22 @@ if (isset($_GET['mode']) && $_GET['mode'] === 'usage') { return '+' + Math.floor(sec / 3600) + t('hourShort'); } - function compactDurationMode() { + function compactDurationMode(context = 'state') { const width = window.innerWidth || document.documentElement.clientWidth || 1024; - if (width <= 380) return 'tight'; - if (width <= 575) return 'compact'; + const target = document.getElementById(context === 'age' ? 'age-seconds' : 'val-state-timer'); + const card = target ? target.closest('.glass-card') : null; + const boxWidth = card + ? card.getBoundingClientRect().width + : (target ? target.getBoundingClientRect().width : 0); + const limitWidth = boxWidth || width; + + if (limitWidth <= 160 || width <= 380) return 'tight'; + if (limitWidth <= 230 || width <= 575) return 'compact'; return 'full'; } function formatResponsiveDuration(sec, context = 'state') { - const mode = compactDurationMode(); + const mode = compactDurationMode(context); if (mode === 'full') { return context === 'age' ? formatAgeUnits(sec) : formatDuration(sec); }