refactor: TBM/작업보고 코드 통합 및 API 쿼리 버그 수정

- 공통 유틸리티 추출 (common/utils.js, common/base-state.js)
- TBM 모바일 인라인 JS/CSS 외부 파일로 분리 (tbm-mobile.js, tbm-mobile.css)
- 미사용 코드 삭제 (index.js, work-report-*.js 등 5개 파일)
- TBM/작업보고 state.js, utils.js를 공통 모듈 기반으로 전환
- 작업보고서 SSO 인증 호환 수정 (token/user 함수)
- tbmModel.js: incomplete-reports 쿼리에서 users→sso_users 조인 수정, leader_name 조인 추가
- docker-compose.yml: system1-web 볼륨 마운트 추가
- 모바일 인계(handover) 기능 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-03-05 07:51:24 +09:00
parent 22a37ac4d9
commit 4388628788
89 changed files with 5296 additions and 5046 deletions

View File

@@ -1,73 +1,28 @@
// sw.js - TK공장관리 Service Worker (network-first)
// 주의: 이 파일을 수정할 때는 반드시 CACHE_VERSION을 올려주세요.
// 잘못된 수정은 사용자 브라우저에 최대 24시간 캐시됩니다.
// 자세한 내용: /docs/PWA-GUIDE.md
// sw.js - Service Worker 자체 해제 (캐시 초기화)
// 기존 SW를 교체하여 모든 캐시를 삭제하고 자체 해제합니다.
const CACHE_VERSION = 'tkfb-v3';
const CACHE_NAME = `tkfb-cache-${CACHE_VERSION}`;
self.addEventListener('install', function(event) {
self.skipWaiting();
});
// 캐시할 정적 리소스 (앱 셸)
const APP_SHELL = [
'/pages/dashboard.html',
'/css/design-system.css',
'/css/mobile.css',
'/img/icon-192x192.png'
];
// 설치: 앱 셸 프리캐시
self.addEventListener('install', (event) => {
self.addEventListener('activate', function(event) {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => cache.addAll(APP_SHELL))
.then(() => self.skipWaiting())
);
});
// 활성화: 이전 버전 캐시 삭제
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys()
.then((keys) => Promise.all(
keys
.filter((key) => key.startsWith('tkfb-cache-') && key !== CACHE_NAME)
.map((key) => caches.delete(key))
))
.then(() => self.clients.claim())
);
});
// 요청 가로채기: network-first 전략
self.addEventListener('fetch', (event) => {
const request = event.request;
// API 요청은 캐시하지 않음 (항상 네트워크)
if (request.url.includes('/api/')) {
return;
}
// 로그인 관련 경로는 캐시하지 않음
if (request.url.includes('/login')) {
return;
}
// GET 요청만 캐시
if (request.method !== 'GET') {
return;
}
event.respondWith(
fetch(request)
.then((response) => {
// 정상 응답이면 캐시에 저장
if (response.ok) {
const clone = response.clone();
caches.open(CACHE_NAME).then((cache) => cache.put(request, clone));
}
return response;
})
.catch(() => {
// 네트워크 실패 시 캐시에서 응답
return caches.match(request);
})
caches.keys().then(function(keys) {
return Promise.all(
keys.map(function(key) {
console.log('SW: 캐시 삭제:', key);
return caches.delete(key);
})
);
}).then(function() {
console.log('SW: 모든 캐시 삭제 완료, 자체 해제');
return self.registration.unregister();
}).then(function() {
return self.clients.matchAll();
}).then(function(clients) {
clients.forEach(function(client) {
client.postMessage({ type: 'SW_CLEARED' });
});
})
);
});