- 백엔드: type_code ANNUAL 매칭 실패 → 전체 합산으로 수정 details에 balance_type, expires_at 포함 - 프론트: 2열 카드 → 통합 리스트 (연차 탭 + 연장근로 행) - 연차 행 클릭 → 상세 모달 (이월/정기/장기/경조사 breakdown) 이월 소진/만료 isExpired() 적용 - 내 메뉴에서 "내 연차 정보" 자동 제거 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
91 lines
3.2 KiB
JavaScript
91 lines
3.2 KiB
JavaScript
/**
|
|
* 대시보드 컨트롤러
|
|
* 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 departmentId = userInfo.department_id;
|
|
const role = userInfo.role;
|
|
|
|
// 2단계: 나머지 3개 병렬 조회 (연차: sp_vacation_balances from user_id)
|
|
const [vacationRows, overtime, quickAccess] = await Promise.all([
|
|
DashboardModel.getVacationBalance(userId, 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,
|
|
balance_type: v.balance_type || 'AUTO',
|
|
expires_at: v.expires_at || null,
|
|
total: parseFloat(v.total_days) || 0,
|
|
used: parseFloat(v.used_days) || 0,
|
|
remaining: parseFloat(v.remaining_days) || 0
|
|
}));
|
|
|
|
// 모든 balance_type 합산
|
|
const totalDays = vacationRows.reduce((s, v) => s + (parseFloat(v.total_days) || 0), 0);
|
|
const usedDays = vacationRows.reduce((s, v) => s + (parseFloat(v.used_days) || 0), 0);
|
|
const remainingDays = totalDays - usedDays;
|
|
|
|
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;
|