해당 서비스 도커화 성공, 룰 추가, 로그인 오류 수정, 소문자 룰 어느정도 해결
This commit is contained in:
170
fastapi-bridge/static/js/attendance.js
Normal file
170
fastapi-bridge/static/js/attendance.js
Normal file
@@ -0,0 +1,170 @@
|
||||
import { API, getAuthHeaders } from '/js/api-config.js';
|
||||
|
||||
const yearSel = document.getElementById('year');
|
||||
const monthSel = document.getElementById('month');
|
||||
const container = document.getElementById('attendanceTableContainer');
|
||||
|
||||
const holidays = [
|
||||
'2025-01-01','2025-01-27','2025-01-28','2025-01-29','2025-01-30','2025-01-31',
|
||||
'2025-03-01','2025-03-03','2025-05-01','2025-05-05','2025-05-06',
|
||||
'2025-06-03','2025-06-06','2025-08-15','2025-10-03','2025-10-09','2025-12-25'
|
||||
];
|
||||
|
||||
const leaveDefaults = {
|
||||
'김두수':16,'임영규':16,'반치원':16,'황인용':16,'표영진':15,
|
||||
'김윤섭':16,'이창호':16,'최광욱':16,'박현수':14,'조윤호':0
|
||||
};
|
||||
|
||||
let workers = [];
|
||||
|
||||
// ✅ 셀렉트 박스 옵션 + 기본 선택 추가
|
||||
function fillSelectOptions() {
|
||||
const currentY = new Date().getFullYear();
|
||||
const currentM = String(new Date().getMonth() + 1).padStart(2, '0');
|
||||
|
||||
for (let y = currentY; y <= currentY + 5; y++) {
|
||||
const selected = y === currentY ? 'selected' : '';
|
||||
yearSel.insertAdjacentHTML('beforeend', `<option value="${y}" ${selected}>${y}</option>`);
|
||||
}
|
||||
|
||||
for (let m = 1; m <= 12; m++) {
|
||||
const mm = String(m).padStart(2, '0');
|
||||
const selected = mm === currentM ? 'selected' : '';
|
||||
monthSel.insertAdjacentHTML('beforeend', `<option value="${mm}" ${selected}>${m}월</option>`);
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ 작업자 목록 불러오기
|
||||
async function fetchWorkers() {
|
||||
try {
|
||||
const res = await fetch(`${API}/workers`, { headers: getAuthHeaders() });
|
||||
workers = await res.json();
|
||||
workers.sort((a, b) => a.worker_id - b.worker_id);
|
||||
} catch (err) {
|
||||
alert('작업자 불러오기 실패');
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ 출근부 불러오기 (해당 연도 전체)
|
||||
async function loadAttendance() {
|
||||
const year = yearSel.value;
|
||||
const month = monthSel.value;
|
||||
if (!year || !month) return alert('연도와 월을 선택하세요');
|
||||
|
||||
const lastDay = new Date(+year, +month, 0).getDate();
|
||||
const start = `${year}-01-01`;
|
||||
const end = `${year}-12-31`;
|
||||
|
||||
try {
|
||||
const res = await fetch(`${API}/workreports?start=${start}&end=${end}`, {
|
||||
headers: getAuthHeaders()
|
||||
});
|
||||
const data = await res.json();
|
||||
renderTable(data, year, month, lastDay);
|
||||
} catch (err) {
|
||||
alert('출근부 로딩 실패');
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ 테이블 렌더링
|
||||
function renderTable(data, year, month, lastDay) {
|
||||
container.innerHTML = '';
|
||||
const weekdays = ['일','월','화','수','목','금','토'];
|
||||
const tbl = document.createElement('table');
|
||||
|
||||
// ⬆️ 헤더 구성
|
||||
let thead = `<thead><tr><th rowspan="2">작업자</th>`;
|
||||
for (let d = 1; d <= lastDay; d++) thead += `<th>${d}</th>`;
|
||||
thead += `<th class="divider" rowspan="2">잔업합계</th><th rowspan="2">사용연차</th><th rowspan="2">잔여연차</th></tr><tr>`;
|
||||
for (let d = 1; d <= lastDay; d++) {
|
||||
const dow = new Date(+year, +month - 1, d).getDay();
|
||||
thead += `<th>${weekdays[dow]}</th>`;
|
||||
}
|
||||
thead += '</tr></thead>';
|
||||
tbl.innerHTML = thead;
|
||||
|
||||
// ⬇️ 본문
|
||||
workers.forEach(w => {
|
||||
// ✅ 월간 데이터 (표에 표시용)
|
||||
const recsThisMonth = data.filter(r =>
|
||||
r.worker_id === w.worker_id &&
|
||||
new Date(r.date).getFullYear() === +year &&
|
||||
new Date(r.date).getMonth() + 1 === +month
|
||||
);
|
||||
|
||||
// ✅ 연간 데이터 (연차 계산용)
|
||||
const recsThisYear = data.filter(r =>
|
||||
r.worker_id === w.worker_id &&
|
||||
new Date(r.date).getFullYear() === +year
|
||||
);
|
||||
|
||||
let otSum = 0;
|
||||
let row = `<tr><td>${w.worker_name}</td>`;
|
||||
|
||||
for (let d = 1; d <= lastDay; d++) {
|
||||
const dd = String(d).padStart(2, '0');
|
||||
const date = `${year}-${month}-${dd}`;
|
||||
|
||||
const rec = recsThisMonth.find(r => {
|
||||
const rDate = new Date(r.date);
|
||||
const yyyy = rDate.getFullYear();
|
||||
const mm = String(rDate.getMonth() + 1).padStart(2, '0');
|
||||
const dd = String(rDate.getDate()).padStart(2, '0');
|
||||
return `${yyyy}-${mm}-${dd}` === date;
|
||||
});
|
||||
|
||||
const dow = new Date(+year, +month - 1, d).getDay();
|
||||
const isWe = dow === 0 || dow === 6;
|
||||
const isHo = holidays.includes(date);
|
||||
|
||||
let txt = '', cls = '';
|
||||
if (rec) {
|
||||
const ot = +rec.overtime_hours || 0;
|
||||
if (ot > 0) {
|
||||
txt = ot; cls = 'overtime-cell'; otSum += ot;
|
||||
} else if (rec.work_details) {
|
||||
const d = rec.work_details;
|
||||
if (['연차','반차','반반차','조퇴'].includes(d)) {
|
||||
txt = d; cls = 'leave';
|
||||
} else if (d === '유급') {
|
||||
txt = d; cls = 'paid-leave';
|
||||
} else if (d === '휴무') {
|
||||
txt = d; cls = 'holiday';
|
||||
} else {
|
||||
txt = d;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
txt = (isWe || isHo) ? '휴무' : '';
|
||||
cls = (isWe || isHo) ? 'holiday' : 'no-data';
|
||||
}
|
||||
|
||||
row += `<td class="${cls}">${txt}</td>`;
|
||||
}
|
||||
|
||||
const usedTot = recsThisYear
|
||||
.filter(r => ['연차','반차','반반차','조퇴'].includes(r.work_details))
|
||||
.reduce((s, r) => s + (
|
||||
r.work_details === '연차' ? 1 :
|
||||
r.work_details === '반차' ? 0.5 :
|
||||
r.work_details === '반반차' ? 0.25 : 0.75
|
||||
), 0);
|
||||
|
||||
const remain = (leaveDefaults[w.worker_name] || 0) - usedTot;
|
||||
|
||||
row += `<td class="divider overtime-sum">${otSum.toFixed(1)}</td>`;
|
||||
row += `<td>${usedTot.toFixed(2)}</td><td>${remain.toFixed(2)}</td></tr>`;
|
||||
row += `<tr class="separator"><td colspan="${lastDay + 4}"></td></tr>`;
|
||||
|
||||
tbl.insertAdjacentHTML('beforeend', row);
|
||||
});
|
||||
|
||||
container.appendChild(tbl);
|
||||
}
|
||||
|
||||
// ✅ 초기 로딩
|
||||
fillSelectOptions();
|
||||
fetchWorkers().then(() => {
|
||||
loadAttendance(); // 자동 조회
|
||||
});
|
||||
document.getElementById('loadAttendance').addEventListener('click', loadAttendance);
|
||||
Reference in New Issue
Block a user