From 492843342a652786cf8ee16e03fc2bb13524c8bb Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Tue, 31 Mar 2026 13:02:16 +0900 Subject: [PATCH] =?UTF-8?q?feat(monthly-comparison):=20detail=20=EB=AA=A8?= =?UTF-8?q?=EB=93=9C=20=EA=B7=BC=ED=83=9C=20=EC=9D=B8=EB=9D=BC=EC=9D=B8=20?= =?UTF-8?q?=ED=8E=B8=EC=A7=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 각 일별 카드에 편집 버튼 (detail 모드 전용) - 인라인 폼: 근무시간 + 휴가유형 선택 - 저장 → POST /attendance/records (upsert + vacation balance 자동 연동) - 휴가유형 선택 시 시간 자동 조정 (연차→0, 반차→4, 반반차→6) - attendance_type_id 자동 결정 Co-Authored-By: Claude Opus 4.6 (1M context) --- .../web/css/monthly-comparison.css | 15 ++++ system1-factory/web/js/monthly-comparison.js | 71 ++++++++++++++++++- .../pages/attendance/monthly-comparison.html | 4 +- 3 files changed, 86 insertions(+), 4 deletions(-) diff --git a/system1-factory/web/css/monthly-comparison.css b/system1-factory/web/css/monthly-comparison.css index 6e8dc21..17d648c 100644 --- a/system1-factory/web/css/monthly-comparison.css +++ b/system1-factory/web/css/monthly-comparison.css @@ -303,3 +303,18 @@ .ds-link { color: #2563eb; font-size: 0.8rem; text-decoration: underline; } @media (max-width: 480px) { body { max-width: 480px; margin: 0 auto; } } + +/* Inline Edit */ +.mc-edit-btn { background: none; border: none; color: #9ca3af; cursor: pointer; font-size: 12px; padding: 2px 6px; margin-left: auto; } +.mc-edit-btn:hover { color: #2563eb; } +.mc-attend-row { display: flex; align-items: center; } +.mc-edit-form { display: flex; flex-direction: column; gap: 6px; padding: 4px 0; } +.mc-edit-row { display: flex; align-items: center; gap: 6px; font-size: 13px; } +.mc-edit-row label { width: 36px; font-weight: 600; color: #6b7280; font-size: 12px; } +.mc-edit-input { width: 60px; padding: 4px 6px; border: 1px solid #d1d5db; border-radius: 6px; font-size: 13px; text-align: center; } +.mc-edit-select { padding: 4px 6px; border: 1px solid #d1d5db; border-radius: 6px; font-size: 13px; flex: 1; } +.mc-edit-actions { display: flex; gap: 6px; margin-top: 2px; } +.mc-edit-save { padding: 4px 12px; background: #10b981; color: white; border: none; border-radius: 6px; font-size: 12px; cursor: pointer; } +.mc-edit-save:hover { background: #059669; } +.mc-edit-cancel { padding: 4px 12px; background: #e5e7eb; color: #374151; border: none; border-radius: 6px; font-size: 12px; cursor: pointer; } +.mc-edit-cancel:hover { background: #d1d5db; } diff --git a/system1-factory/web/js/monthly-comparison.js b/system1-factory/web/js/monthly-comparison.js index ff6c2e9..187b24f 100644 --- a/system1-factory/web/js/monthly-comparison.js +++ b/system1-factory/web/js/monthly-comparison.js @@ -253,9 +253,11 @@ function renderDailyList(records) { if (r.attendance) { const vacInfo = r.attendance.vacation_type ? ` (${r.attendance.vacation_type})` : ''; - attendLine = `
근태관리: ${r.attendance.total_work_hours}h (${escHtml(r.attendance.attendance_type)}${vacInfo})
`; + const editBtn = currentMode === 'detail' ? `` : ''; + attendLine = `
근태관리: ${r.attendance.total_work_hours}h (${escHtml(r.attendance.attendance_type)}${vacInfo})${editBtn}
`; } else if (r.status !== 'holiday') { - attendLine = '
근태관리: 미입력
'; + const addBtn = currentMode === 'detail' ? `` : ''; + attendLine = `
근태관리: 미입력${addBtn}
`; } if (r.status === 'mismatch' && r.hours_diff) { @@ -549,6 +551,71 @@ function showToast(msg, type) { setTimeout(() => t.remove(), 3000); } +// ===== Inline Attendance Edit (detail mode) ===== +function getAttendanceTypeId(hours, vacTypeId) { + if (vacTypeId) return 4; // VACATION + if (hours >= 8) return 1; // REGULAR + if (hours > 0) return 3; // PARTIAL + return 0; +} + +function editAttendance(date, currentHours, currentVacTypeId) { + const el = document.getElementById('attend-' + date); + if (!el) return; + const vacTypeId = currentVacTypeId === 'null' || currentVacTypeId === null ? '' : currentVacTypeId; + el.innerHTML = ` +
+
+ + + h +
+
+ + +
+
+ + +
+
+ `; +} + +function onVacTypeChange(date) { + const vacType = document.getElementById('editVacType-' + date).value; + const hoursInput = document.getElementById('editHours-' + date); + if (vacType === '1') hoursInput.value = '0'; // 연차 → 0시간 + else if (vacType === '2') hoursInput.value = '4'; // 반차 → 4시간 + else if (vacType === '3') hoursInput.value = '6'; // 반반차 → 6시간 +} + +async function saveAttendance(date) { + const hours = parseFloat(document.getElementById('editHours-' + date).value) || 0; + const vacTypeVal = document.getElementById('editVacType-' + date).value; + const vacTypeId = vacTypeVal ? parseInt(vacTypeVal) : null; + const attTypeId = getAttendanceTypeId(hours, vacTypeId); + + try { + await window.apiCall('/attendance/records', 'POST', { + record_date: date, + user_id: currentUserId, + total_work_hours: hours, + vacation_type_id: vacTypeId, + attendance_type_id: attTypeId + }); + showToast('근태 수정 완료', 'success'); + await loadData(); // 전체 새로고침 + } catch (e) { + showToast('저장 실패: ' + (e.message || e), 'error'); + } +} + // ESC로 모달 닫기 document.addEventListener('keydown', e => { if (e.key === 'Escape') closeRejectModal(); diff --git a/system1-factory/web/pages/attendance/monthly-comparison.html b/system1-factory/web/pages/attendance/monthly-comparison.html index 7e8b52f..0a72520 100644 --- a/system1-factory/web/pages/attendance/monthly-comparison.html +++ b/system1-factory/web/pages/attendance/monthly-comparison.html @@ -8,7 +8,7 @@ - +
@@ -159,7 +159,7 @@ - +