feat: Sprint 002 리뷰 수정 + Sprint 003 대시보드 API/UI 구현
Sprint 002: - proxyInput created_by_name 누락 수정 - tbm-mobile 하단 네비에 현황 탭 추가 - proxy-input 저장 버튼 스피너 추가 Sprint 003: - GET /api/dashboard/my-summary API (연차/연장근로/페이지 통합) - 생산팀 대시보드 UI (프로필카드 + 아이콘 그리드) - dashboard-new.html 교체 (기존 .bak 백업) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
89
system1-factory/api/controllers/dashboardController.js
Normal file
89
system1-factory/api/controllers/dashboardController.js
Normal file
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* 대시보드 컨트롤러
|
||||
* Sprint 003 — 개인 요약 API
|
||||
*/
|
||||
const DashboardModel = require('../models/dashboardModel');
|
||||
const logger = require('../../shared/utils/logger');
|
||||
|
||||
const DashboardController = {
|
||||
/**
|
||||
* GET /api/dashboard/my-summary
|
||||
* 연차 잔여 + 월간 연장근로 + 접근 가능 페이지
|
||||
*/
|
||||
getMySummary: async (req, res) => {
|
||||
try {
|
||||
const userId = req.user.user_id || req.user.id;
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = now.getMonth() + 1;
|
||||
|
||||
// 1단계: 사용자 정보 먼저 조회 (worker_id 필요)
|
||||
const userInfo = await DashboardModel.getUserInfo(userId);
|
||||
if (!userInfo) {
|
||||
return res.status(404).json({ success: false, message: '사용자 정보를 찾을 수 없습니다.' });
|
||||
}
|
||||
|
||||
const workerId = userInfo.worker_id;
|
||||
const departmentId = userInfo.department_id;
|
||||
const role = userInfo.role;
|
||||
|
||||
// 2단계: 나머지 3개 병렬 조회
|
||||
const [vacationRows, overtime, quickAccess] = await Promise.all([
|
||||
DashboardModel.getVacationBalance(workerId, year),
|
||||
DashboardModel.getMonthlyOvertime(userId, year, month),
|
||||
DashboardModel.getQuickAccess(userId, departmentId, role)
|
||||
]);
|
||||
|
||||
// 연차 응답 가공
|
||||
const details = vacationRows.map(v => ({
|
||||
type_name: v.type_name,
|
||||
type_code: v.type_code,
|
||||
total: parseFloat(v.total_days) || 0,
|
||||
used: parseFloat(v.used_days) || 0,
|
||||
remaining: parseFloat(v.remaining_days) || 0
|
||||
}));
|
||||
|
||||
const annualRow = vacationRows.find(v => v.type_code === 'ANNUAL');
|
||||
const totalDays = annualRow ? parseFloat(annualRow.total_days) || 0 : 0;
|
||||
const usedDays = annualRow ? parseFloat(annualRow.used_days) || 0 : 0;
|
||||
const remainingDays = annualRow ? parseFloat(annualRow.remaining_days) || 0 : 0;
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
user: {
|
||||
user_id: userInfo.user_id,
|
||||
name: userInfo.name,
|
||||
worker_name: userInfo.worker_name || userInfo.name,
|
||||
job_type: userInfo.job_type || '',
|
||||
department_name: userInfo.department_name,
|
||||
department_id: userInfo.department_id,
|
||||
role: userInfo.role
|
||||
},
|
||||
vacation: {
|
||||
year,
|
||||
total_days: totalDays,
|
||||
used_days: usedDays,
|
||||
remaining_days: remainingDays,
|
||||
details
|
||||
},
|
||||
overtime: {
|
||||
year,
|
||||
month,
|
||||
total_overtime_hours: parseFloat(overtime.total_overtime_hours) || 0,
|
||||
overtime_days: parseInt(overtime.overtime_days) || 0,
|
||||
total_work_days: parseInt(overtime.total_work_days) || 0,
|
||||
total_work_hours: parseFloat(overtime.total_work_hours) || 0,
|
||||
avg_daily_hours: parseFloat(parseFloat(overtime.avg_daily_hours || 0).toFixed(1))
|
||||
},
|
||||
quick_access: quickAccess
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error('대시보드 요약 조회 오류:', err);
|
||||
res.status(500).json({ success: false, message: '대시보드 데이터 조회 중 오류가 발생했습니다.', error: err.message });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = DashboardController;
|
||||
Reference in New Issue
Block a user