feat: TBM 모바일 시스템 + 작업 분할/이동 + 권한 통합

TBM 시스템:
- 4단계 워크플로우 (draft→세부편집→완료→작업보고)
- 모바일 전용 TBM 페이지 (tbm-mobile.html) + 3단계 생성 위자드
- 작업자 작업 분할 (work_hours + split_seq)
- 작업자 이동 보내기/빼오기 (tbm_transfers 테이블)
- 생성 시 중복 배정 방지 (당일 배정 현황 조회)
- 데스크탑 TBM 페이지 세부편집 기능 추가

작업보고서:
- 모바일 전용 작업보고서 페이지 (report-create-mobile.html)
- TBM에서 사전 등록된 work_hours 자동 반영

권한 시스템:
- tkuser user_page_permissions 테이블과 system1 페이지 접근 연동
- pageAccessRoutes를 userRoutes보다 먼저 등록 (라우트 우선순위 수정)
- TKUSER_DEFAULT_ACCESS 폴백 추가 (개인→부서→기본값 3단계)
- 권한 캐시키 갱신 (userPageAccess_v2)

기타:
- app-init.js 캐시 버스팅 (v=5)
- iOS Safari touch-action: manipulation 적용
- KST 타임존 날짜 버그 수정 (toISOString UTC 이슈)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-02-25 07:46:21 +09:00
parent d36303101e
commit 7637be33f3
65 changed files with 9470 additions and 240 deletions

View File

@@ -26,7 +26,7 @@
if (window.clearSSOAuth) { window.clearSSOAuth(); return; }
localStorage.removeItem('sso_token');
localStorage.removeItem('sso_user');
localStorage.removeItem('userPageAccess');
localStorage.removeItem('userPageAccess_v2');
}
// ===== 페이지 권한 캐시 =====
@@ -36,7 +36,7 @@
if (!currentUser || !currentUser.user_id) return null;
// 캐시 확인
const cached = localStorage.getItem('userPageAccess');
const cached = localStorage.getItem('userPageAccess_v2_v2');
if (cached) {
try {
const cacheData = JSON.parse(cached);
@@ -44,7 +44,7 @@
return cacheData.pages;
}
} catch (e) {
localStorage.removeItem('userPageAccess');
localStorage.removeItem('userPageAccess_v2');
}
}
@@ -67,7 +67,7 @@
const data = await response.json();
const pages = data.data.pageAccess || [];
localStorage.setItem('userPageAccess', JSON.stringify({
localStorage.setItem('userPageAccess_v2', JSON.stringify({
pages: pages,
timestamp: Date.now()
}));
@@ -91,11 +91,19 @@
}
// ===== 현재 페이지 키 추출 =====
// 하위 페이지 → 부모 페이지 키 매핑 (동일 권한 공유)
var PAGE_KEY_ALIASES = {
'work.tbm-create': 'work.tbm',
'work.tbm-mobile': 'work.tbm',
'work.report-create-mobile': 'work.report-create'
};
function getCurrentPageKey() {
const path = window.location.pathname;
if (!path.startsWith('/pages/')) return null;
const pagePath = path.substring(7).replace('.html', '');
return pagePath.replace(/\//g, '.');
const rawKey = pagePath.replace(/\//g, '.');
return PAGE_KEY_ALIASES[rawKey] || rawKey;
}
// ===== 컴포넌트 로더 =====