Files
tk-factory-services/system3-nonconformance/web/static/js/m/m-common.js
Hyungi Ahn 9b81a52283 feat(system3): TKQC 모바일 전용 페이지 구현 및 데스크탑 관리함 반응형 개선
- 모바일 전용 페이지 신규: /m/dashboard, /m/inbox, /m/management
- 공통 모바일 CSS/JS: m-common.css, m-common.js (바텀시트, 바텀네비, 터치 최적화)
- nginx.conf에 /m/ location 블록 추가
- 데스크탑 HTML에 모바일 뷰포트 리다이렉트 추가 (<=768px)
- 데스크탑 관리함 카드 헤더 반응형 레이아웃 (flex-wrap, 1280px 브레이크포인트)
- collapse-content overflow:hidden → overflow:visible 수정 (내용 잘림 해결)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 13:34:52 +09:00

196 lines
7.0 KiB
JavaScript

/**
* m-common.js — TKQC 모바일 공통 JS
* 바텀 네비게이션, 바텀시트 엔진, 인증, 뷰포트 가드, 토스트
*/
/* ===== Viewport Guard: 데스크탑이면 리다이렉트 ===== */
(function () {
if (window.innerWidth > 768) {
var page = location.pathname.replace('/m/', '').replace('.html', '');
var map = { dashboard: '/issues-dashboard.html', inbox: '/issues-inbox.html', management: '/issues-management.html' };
window.location.replace(map[page] || '/issues-dashboard.html');
}
})();
/* ===== KST Date Utilities ===== */
function getKSTDate(date) {
var d = new Date(date);
return new Date(d.getTime() + 9 * 60 * 60 * 1000);
}
function formatKSTDate(date) {
return new Date(date).toLocaleDateString('ko-KR', { timeZone: 'Asia/Seoul' });
}
function formatKSTTime(date) {
return new Date(date).toLocaleTimeString('ko-KR', { timeZone: 'Asia/Seoul', hour: '2-digit', minute: '2-digit' });
}
function formatKSTDateTime(date) {
return new Date(date).toLocaleString('ko-KR', { timeZone: 'Asia/Seoul', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' });
}
function getKSTToday() {
var now = new Date();
var kst = getKSTDate(now);
return new Date(kst.getFullYear(), kst.getMonth(), kst.getDate());
}
function getTimeAgo(date) {
var now = getKSTDate(new Date());
var d = getKSTDate(date);
var diff = now - d;
var mins = Math.floor(diff / 60000);
var hours = Math.floor(diff / 3600000);
var days = Math.floor(diff / 86400000);
if (mins < 1) return '방금 전';
if (mins < 60) return mins + '분 전';
if (hours < 24) return hours + '시간 전';
if (days < 7) return days + '일 전';
return formatKSTDate(date);
}
/* ===== Bottom Navigation ===== */
function renderBottomNav(activePage) {
var nav = document.createElement('nav');
nav.className = 'm-bottom-nav';
var items = [
{ icon: 'fa-chart-line', label: '현황판', href: '/m/dashboard.html', page: 'dashboard' },
{ icon: 'fa-inbox', label: '수신함', href: '/m/inbox.html', page: 'inbox' },
{ icon: 'fa-tasks', label: '관리함', href: '/m/management.html', page: 'management' },
{ icon: 'fa-bullhorn', label: '신고', href: 'https://tkreport.technicalkorea.net', page: 'report', external: true, highlight: true }
];
items.forEach(function (item) {
var a = document.createElement('a');
a.href = item.href;
a.className = 'm-nav-item';
if (item.page === activePage) a.classList.add('active');
if (item.highlight) a.classList.add('highlight');
if (item.external) { a.target = '_blank'; a.rel = 'noopener'; }
a.innerHTML = '<i class="fas ' + item.icon + '"></i><span>' + item.label + '</span>';
nav.appendChild(a);
});
document.body.appendChild(nav);
}
/* ===== Bottom Sheet Engine ===== */
var _activeSheets = [];
function openSheet(sheetId) {
var overlay = document.getElementById(sheetId + 'Overlay');
var sheet = document.getElementById(sheetId + 'Sheet');
if (!overlay || !sheet) return;
overlay.classList.add('open');
sheet.classList.add('open');
document.body.style.overflow = 'hidden';
_activeSheets.push(sheetId);
}
function closeSheet(sheetId) {
var overlay = document.getElementById(sheetId + 'Overlay');
var sheet = document.getElementById(sheetId + 'Sheet');
if (!overlay || !sheet) return;
overlay.classList.remove('open');
sheet.classList.remove('open');
_activeSheets = _activeSheets.filter(function (id) { return id !== sheetId; });
if (_activeSheets.length === 0) document.body.style.overflow = '';
}
function closeAllSheets() {
_activeSheets.slice().forEach(function (id) { closeSheet(id); });
}
// ESC key closes topmost sheet
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape' && _activeSheets.length) {
closeSheet(_activeSheets[_activeSheets.length - 1]);
}
});
/* ===== Toast ===== */
var _toastTimer = null;
function showToast(message, type, duration) {
type = type || 'info';
duration = duration || 3000;
var existing = document.querySelector('.m-toast');
if (existing) existing.remove();
clearTimeout(_toastTimer);
var toast = document.createElement('div');
toast.className = 'm-toast';
if (type !== 'info') toast.classList.add(type);
toast.textContent = message;
document.body.appendChild(toast);
requestAnimationFrame(function () { toast.classList.add('show'); });
_toastTimer = setTimeout(function () {
toast.classList.remove('show');
setTimeout(function () { toast.remove(); }, 300);
}, duration);
}
/* ===== Photo Modal ===== */
function openPhotoModal(src) {
var modal = document.getElementById('photoModal');
if (!modal) {
modal = document.createElement('div');
modal.id = 'photoModal';
modal.className = 'm-photo-modal';
modal.innerHTML = '<button class="m-photo-modal-close" onclick="closePhotoModal()"><i class="fas fa-times"></i></button><img>';
modal.addEventListener('click', function (e) { if (e.target === modal) closePhotoModal(); });
document.body.appendChild(modal);
}
modal.querySelector('img').src = src;
modal.classList.add('open');
document.body.style.overflow = 'hidden';
}
function closePhotoModal() {
var modal = document.getElementById('photoModal');
if (modal) {
modal.classList.remove('open');
if (!_activeSheets.length) document.body.style.overflow = '';
}
}
/* ===== Auth Helper ===== */
async function mCheckAuth() {
var token = TokenManager.getToken();
if (!token) {
window.location.href = _getLoginUrl();
return null;
}
try {
var user = await AuthAPI.getCurrentUser();
return user;
} catch (e) {
TokenManager.removeToken();
TokenManager.removeUser();
window.location.href = _getLoginUrl();
return null;
}
}
/* ===== Loading Overlay ===== */
function hideLoading() {
var el = document.getElementById('loadingOverlay');
if (el) { el.classList.add('hide'); setTimeout(function () { el.remove(); }, 300); }
}
/* ===== Helpers ===== */
function escapeHtml(text) {
if (!text) return '';
var div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function getPhotoPaths(issue) {
return [issue.photo_path, issue.photo_path2, issue.photo_path3, issue.photo_path4, issue.photo_path5].filter(Boolean);
}
function getCompletionPhotoPaths(issue) {
return [issue.completion_photo_path, issue.completion_photo_path2, issue.completion_photo_path3, issue.completion_photo_path4, issue.completion_photo_path5].filter(Boolean);
}
function renderPhotoThumbs(photos) {
if (!photos || !photos.length) return '';
return '<div class="m-photo-row">' + photos.map(function (p, i) {
return '<img src="' + p + '" class="m-photo-thumb" onclick="openPhotoModal(\'' + p + '\')" alt="사진 ' + (i + 1) + '">';
}).join('') + '</div>';
}