feat: 모바일 UX 대폭 개선 + PWA 구현 + 로그인 루프 수정
- 모바일 하단 네비: 메뉴 제거, 4개 핵심 기능(홈/TBM/작업보고/출근) SVG 아이콘 - 모바일 사이드바 스킵: 768px 이하에서 사이드바 미로드, 레이아웃 오프셋 해결 - 모바일 헤더: 햄버거 메뉴 숨김, 본문 margin/overflow 정리 - TBM 모바일: 풀스크린 모달, 저장 버튼 하단 고정, 터치 UX 개선 - PWA: manifest.json, sw.js(network-first), 앱 아이콘, iOS 메타태그, 킬스위치 - 로그인 무한루프 수정: 토큰 만료 검증, 쿠키 정리, loginPage 경로 수정 - 신고 메뉴 tkreport 리다이렉트: navbar + sidebar cross-system-link 적용 - TBM API: 작업장별 안전점검 체크리스트 조회 엔드포인트 추가 - 안전점검 체크리스트 관리 UI 개선 - tkuser: 이슈유형 관리 기능 추가 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -48,7 +48,7 @@ function setupRoutes(app) {
|
||||
const vacationTypeRoutes = require('../routes/vacationTypeRoutes');
|
||||
const vacationBalanceRoutes = require('../routes/vacationBalanceRoutes');
|
||||
const visitRequestRoutes = require('../routes/visitRequestRoutes');
|
||||
// workIssueRoutes removed - moved to System 2 (신고 시스템)
|
||||
const workIssueRoutes = require('../routes/workIssueRoutes');
|
||||
const departmentRoutes = require('../routes/departmentRoutes');
|
||||
const patrolRoutes = require('../routes/patrolRoutes');
|
||||
const notificationRoutes = require('../routes/notificationRoutes');
|
||||
@@ -159,14 +159,7 @@ function setupRoutes(app) {
|
||||
app.use('/api/workplace-visits', visitRequestRoutes); // 출입 신청 및 안전교육 관리
|
||||
app.use('/api', pageAccessRoutes); // 페이지 접근 권한 관리
|
||||
app.use('/api/tbm', tbmRoutes); // TBM 시스템
|
||||
// work-issues moved to System 2 - redirect
|
||||
app.use('/api/work-issues', (req, res) => {
|
||||
res.status(301).json({
|
||||
success: false,
|
||||
error: '신고 시스템이 분리되었습니다',
|
||||
redirect: '/report/api/work-issues' + req.url
|
||||
});
|
||||
});
|
||||
app.use('/api/work-issues', workIssueRoutes); // 카테고리/아이템 + 신고 조회 (같은 MariaDB 공유)
|
||||
app.use('/api/departments', departmentRoutes); // 부서 관리
|
||||
app.use('/api/patrol', patrolRoutes); // 일일순회점검 시스템
|
||||
app.use('/api/notifications', notificationRoutes); // 알림 시스템
|
||||
|
||||
@@ -176,6 +176,36 @@ const TbmController = {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* TBM 세션 삭제 (draft 상태만)
|
||||
*/
|
||||
deleteSession: (req, res) => {
|
||||
const { sessionId } = req.params;
|
||||
|
||||
TbmModel.deleteSession(sessionId, (err, result) => {
|
||||
if (err) {
|
||||
console.error('TBM 세션 삭제 오류:', err);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: 'TBM 세션 삭제 중 오류가 발생했습니다.',
|
||||
error: err.message
|
||||
});
|
||||
}
|
||||
|
||||
if (result.affectedRows === 0) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'TBM 세션을 찾을 수 없거나 이미 완료된 세션입니다.'
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'TBM 세션이 삭제되었습니다.'
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// ==================== 팀 구성 관련 ====================
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,7 +26,7 @@ exports.getAllCategories = (req, res) => {
|
||||
exports.getCategoriesByType = (req, res) => {
|
||||
const { type } = req.params;
|
||||
|
||||
if (!['nonconformity', 'safety'].includes(type)) {
|
||||
if (!['nonconformity', 'safety', 'facility'].includes(type)) {
|
||||
return res.status(400).json({ success: false, error: '유효하지 않은 카테고리 타입입니다.' });
|
||||
}
|
||||
|
||||
|
||||
@@ -97,22 +97,36 @@ const TbmModel = {
|
||||
w.worker_name as leader_name,
|
||||
w.job_type as leader_job_type,
|
||||
w.phone_number as leader_phone,
|
||||
p.project_name,
|
||||
p.job_no,
|
||||
p.site,
|
||||
wt.name as work_type_name,
|
||||
wt.category as work_type_category,
|
||||
t.task_name,
|
||||
t.description as task_description,
|
||||
u.username as created_by_username,
|
||||
u.name as created_by_name
|
||||
u.name as created_by_name,
|
||||
COUNT(DISTINCT ta.worker_id) as team_member_count,
|
||||
first_p.project_name,
|
||||
first_p.job_no,
|
||||
first_wt.name as work_type_name,
|
||||
first_wt.category as work_type_category,
|
||||
first_t.task_name,
|
||||
first_t.description as task_description,
|
||||
first_wp.workplace_name as work_location,
|
||||
first_wc.category_name as workplace_category_name
|
||||
FROM tbm_sessions s
|
||||
LEFT JOIN workers w ON s.leader_id = w.worker_id
|
||||
LEFT JOIN projects p ON s.project_id = p.project_id
|
||||
LEFT JOIN work_types wt ON s.work_type_id = wt.id
|
||||
LEFT JOIN tasks t ON s.task_id = t.task_id
|
||||
LEFT JOIN users u ON s.created_by = u.user_id
|
||||
LEFT JOIN tbm_team_assignments ta ON s.session_id = ta.session_id
|
||||
LEFT JOIN (
|
||||
SELECT * FROM tbm_team_assignments
|
||||
WHERE (session_id, assignment_id) IN (
|
||||
SELECT session_id, MIN(assignment_id)
|
||||
FROM tbm_team_assignments
|
||||
GROUP BY session_id
|
||||
)
|
||||
) first_ta ON s.session_id = first_ta.session_id
|
||||
LEFT JOIN projects first_p ON first_ta.project_id = first_p.project_id
|
||||
LEFT JOIN work_types first_wt ON first_ta.work_type_id = first_wt.id
|
||||
LEFT JOIN tasks first_t ON first_ta.task_id = first_t.task_id
|
||||
LEFT JOIN workplaces first_wp ON first_ta.workplace_id = first_wp.workplace_id
|
||||
LEFT JOIN workplace_categories first_wc ON first_ta.workplace_category_id = first_wc.category_id
|
||||
WHERE s.session_id = ?
|
||||
GROUP BY s.session_id
|
||||
`;
|
||||
|
||||
const [rows] = await db.query(sql, [sessionId]);
|
||||
@@ -174,6 +188,23 @@ const TbmModel = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* TBM 세션 삭제 (draft 상태만 가능)
|
||||
*/
|
||||
deleteSession: async (sessionId, callback) => {
|
||||
try {
|
||||
const db = await getDb();
|
||||
// draft 상태인 세션만 삭제 허용
|
||||
const [result] = await db.query(
|
||||
`DELETE FROM tbm_sessions WHERE session_id = ? AND status = 'draft'`,
|
||||
[sessionId]
|
||||
);
|
||||
callback(null, result);
|
||||
} catch (err) {
|
||||
callback(err);
|
||||
}
|
||||
},
|
||||
|
||||
// ==================== 팀 구성 관련 ====================
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,6 +24,9 @@ router.put('/sessions/:sessionId', requireAuth, TbmController.updateSession);
|
||||
// TBM 세션 완료 처리
|
||||
router.post('/sessions/:sessionId/complete', requireAuth, TbmController.completeSession);
|
||||
|
||||
// TBM 세션 삭제 (draft 상태만)
|
||||
router.delete('/sessions/:sessionId', requireAuth, TbmController.deleteSession);
|
||||
|
||||
// ==================== 팀 구성 관련 ====================
|
||||
|
||||
// 팀원 추가 (단일)
|
||||
|
||||
Reference in New Issue
Block a user