Compact car monitor durations by card width

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