185 lines
5.6 KiB
JavaScript
185 lines
5.6 KiB
JavaScript
import { renderCalendar } from '/js/calendar.js'; // 날짜 캘린더 모듈
|
|
import { API, getAuthHeaders, ensureAuthenticated } from '/js/api-config.js';
|
|
|
|
// 인증 확인
|
|
ensureAuthenticated();
|
|
|
|
// ✅ DOM 요소
|
|
const reportBody = document.getElementById('reportBody');
|
|
const submitBtn = document.getElementById('submitBtn');
|
|
const defaultProjectId = '13';
|
|
const defaultTaskId = '15';
|
|
let selectedDateStr = '';
|
|
|
|
// ✅ 페이지 로드시 초기 렌더링
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
fetch('/components/navbar.html')
|
|
.then(r => r.text())
|
|
.then(html => {
|
|
document.getElementById('navbar-container').innerHTML = html;
|
|
})
|
|
.catch(err => console.error('🔴 네비게이션 바 로딩 실패:', err));
|
|
|
|
renderCalendar('calendar', date => {
|
|
selectedDateStr = date;
|
|
loadWorkers();
|
|
});
|
|
});
|
|
|
|
// ✅ 작업자, 프로젝트, 작업 불러오기
|
|
async function loadWorkers() {
|
|
if (!selectedDateStr) return;
|
|
|
|
try {
|
|
const [wrRes, prRes, tkRes] = await Promise.all([
|
|
fetch(`${API}/workers`, { headers: getAuthHeaders() }),
|
|
fetch(`${API}/projects`, { headers: getAuthHeaders() }),
|
|
fetch(`${API}/tasks`, { headers: getAuthHeaders() })
|
|
]);
|
|
|
|
if (!wrRes.ok || !prRes.ok || !tkRes.ok) {
|
|
throw new Error('데이터 불러오기 실패');
|
|
}
|
|
|
|
const workers = await wrRes.json();
|
|
const projects = await prRes.json();
|
|
const tasks = await tkRes.json();
|
|
|
|
// 배열 체크
|
|
if (!Array.isArray(workers) || !Array.isArray(projects) || !Array.isArray(tasks)) {
|
|
throw new Error('잘못된 데이터 형식');
|
|
}
|
|
|
|
workers.sort((a, b) => a.worker_id - b.worker_id);
|
|
reportBody.innerHTML = '';
|
|
|
|
workers.forEach((w, i) => {
|
|
const tr = document.createElement('tr');
|
|
tr.innerHTML = `
|
|
<td>${i + 1}</td>
|
|
<td>
|
|
<input type="hidden" name="worker_id" value="${w.worker_id}">
|
|
${w.worker_name}
|
|
</td>
|
|
<td>
|
|
<select name="project_id">
|
|
${projects.map(p =>
|
|
`<option value="${p.project_id}">${p.project_name}</option>`
|
|
).join('')}
|
|
</select>
|
|
</td>
|
|
<td>
|
|
<select name="task_id">
|
|
${tasks.map(t =>
|
|
`<option value="${t.task_id}">${t.category}:${t.subcategory}</option>`
|
|
).join('')}
|
|
</select>
|
|
</td>
|
|
<td>
|
|
<select name="overtime">
|
|
<option value="">없음</option>
|
|
<option>1</option><option>2</option>
|
|
<option>3</option><option>4</option>
|
|
</select>
|
|
</td>
|
|
<td>
|
|
<select name="work_type">
|
|
<option>근무</option><option>연차</option><option>유급</option>
|
|
<option>반차</option><option>반반차</option><option>조퇴</option>
|
|
<option>휴무</option>
|
|
</select>
|
|
</td>
|
|
<td>
|
|
<input type="text" name="memo" placeholder="메모">
|
|
</td>
|
|
<td>
|
|
<button class="remove-btn">x</button>
|
|
</td>
|
|
`;
|
|
reportBody.appendChild(tr);
|
|
|
|
// 근무형태 변경시 프로젝트/작업 필드 비활성화
|
|
const workSel = tr.querySelector('[name="work_type"]');
|
|
const projSel = tr.querySelector('[name="project_id"]');
|
|
const taskSel = tr.querySelector('[name="task_id"]');
|
|
|
|
workSel.addEventListener('change', () => {
|
|
const disabled = ['연차','휴무','유급'].includes(workSel.value);
|
|
projSel.value = disabled ? defaultProjectId : projSel.value;
|
|
taskSel.value = disabled ? defaultTaskId : taskSel.value;
|
|
projSel.disabled = taskSel.disabled = disabled;
|
|
});
|
|
|
|
tr.querySelector('.remove-btn').addEventListener('click', () => {
|
|
tr.remove();
|
|
updateRowNumbers();
|
|
});
|
|
});
|
|
} catch (err) {
|
|
console.error(err);
|
|
alert(err.message || '작업자 불러오기 중 오류 발생');
|
|
}
|
|
}
|
|
|
|
// ✅ 행 번호 다시 매기기
|
|
function updateRowNumbers() {
|
|
reportBody.querySelectorAll('tr').forEach((tr, i) => {
|
|
tr.children[0].textContent = i + 1;
|
|
});
|
|
}
|
|
|
|
// ✅ 전체 등록 처리
|
|
submitBtn.addEventListener('click', async () => {
|
|
if (!selectedDateStr) {
|
|
alert('날짜를 먼저 선택하세요.');
|
|
return;
|
|
}
|
|
|
|
const rows = Array.from(reportBody.querySelectorAll('tr'));
|
|
if (rows.length === 0) {
|
|
alert('등록할 작업자가 없습니다.');
|
|
return;
|
|
}
|
|
|
|
const seen = new Set();
|
|
const payload = [];
|
|
|
|
for (let tr of rows) {
|
|
const wid = tr.querySelector('[name="worker_id"]').value;
|
|
if (seen.has(wid)) {
|
|
alert('중복된 작업자가 있습니다.');
|
|
return;
|
|
}
|
|
seen.add(wid);
|
|
|
|
payload.push({
|
|
date: selectedDateStr,
|
|
worker_id: wid,
|
|
project_id: tr.querySelector('[name="project_id"]').value,
|
|
task_id: tr.querySelector('[name="task_id"]').value,
|
|
overtime_hours: tr.querySelector('[name="overtime"]').value,
|
|
work_details: tr.querySelector('[name="work_type"]').value,
|
|
memo: tr.querySelector('[name="memo"]').value
|
|
});
|
|
}
|
|
|
|
try {
|
|
const res = await fetch(`${API}/workreports`, {
|
|
method: 'POST',
|
|
headers: getAuthHeaders(),
|
|
body: JSON.stringify(payload)
|
|
});
|
|
const result = await res.json();
|
|
|
|
if (result.success) {
|
|
alert('✅ 등록 완료!');
|
|
// 선택적: 페이지 새로고침 또는 다른 날짜로 이동
|
|
// loadWorkers();
|
|
} else {
|
|
alert('❌ 등록 실패: ' + (result.error || '알 수 없는 오류'));
|
|
}
|
|
} catch (err) {
|
|
console.error(err);
|
|
alert('서버 오류가 발생했습니다: ' + err.message);
|
|
}
|
|
}); |