/** * =============================================================================== * FILE: modules/web_api/static/js/manual_absences.js * ROLE: Модальные окна "Мест. командир.", "Иное", универсальный автокомплит ФИО * и синхронизация с боковой панелью SidebarManager. * =============================================================================== */ let activeAbsenceType = 'LOCAL_TRIP'; // 'LOCAL_TRIP' или 'OTHER' let reasonsCache = []; async function loadAbsenceReasons() { try { const res = await fetch('/api/v1/manual-absences/reasons'); if (res.ok) { const data = await res.json(); reasonsCache = data.reasons || []; } } catch (e) { console.error('Ошибка загрузки причин:', e); } } // ⭐️ Открытие окна: режим создания (item = null) или редактирования (item = {...}) function openManualAbsenceModal(type, editItem = null) { activeAbsenceType = type; const isTrip = (type === 'LOCAL_TRIP'); const modal = document.getElementById('manual-absence-modal'); const titleEl = document.getElementById('manual-absence-modal-title-text'); const reasonBlock = document.getElementById('manual-absence-reason-block'); const reasonSelect = document.getElementById('manual-absence-reason-select'); const idInput = document.getElementById('manual-absence-id'); const fioInput = document.getElementById('manual-absence-fio-input'); const deptInput = document.getElementById('manual-absence-dept'); const posInput = document.getElementById('manual-absence-pos'); const startDateInput = document.getElementById('manual-absence-start-date'); const endDateInput = document.getElementById('manual-absence-end-date'); const errEl = document.getElementById('manual-absence-error'); if (errEl) errEl.classList.add('hidden'); if (reasonBlock && reasonSelect) { if (isTrip) { reasonBlock.classList.add('hidden'); } else { reasonBlock.classList.remove('hidden'); reasonSelect.innerHTML = reasonsCache.map(r => ``).join(''); } } const today = new Date().toISOString().split('T')[0]; if (editItem) { // Режим РЕДАКТИРОВАНИЯ if (titleEl) titleEl.innerText = isTrip ? 'Изменение сроков командировки' : 'Изменение сроков отсутствия'; if (idInput) idInput.value = editItem.id; if (fioInput) { fioInput.value = editItem.fio; fioInput.readOnly = true; fioInput.classList.add('bg-slate-100', 'text-slate-500', 'cursor-not-allowed'); } if (deptInput) deptInput.value = editItem.department || ''; if (posInput) posInput.value = editItem.position || ''; if (startDateInput) startDateInput.value = editItem.date_start ? editItem.date_start.replace(/\./g, '-') : today; if (endDateInput) endDateInput.value = editItem.date_end ? editItem.date_end.replace(/\./g, '-') : today; if (reasonSelect && editItem.reason) reasonSelect.value = editItem.reason; } else { // Режим СОЗДАНИЯ if (titleEl) titleEl.innerText = isTrip ? 'Добавить в командировки' : 'Добавить отсутствие'; if (idInput) idInput.value = ''; if (fioInput) { fioInput.value = ''; fioInput.readOnly = false; fioInput.classList.remove('bg-slate-100', 'text-slate-500', 'cursor-not-allowed'); } if (deptInput) deptInput.value = ''; if (posInput) posInput.value = ''; if (startDateInput) startDateInput.value = today; if (endDateInput) endDateInput.value = today; } if (modal) modal.classList.remove('hidden'); } function closeManualAbsenceModal() { const modal = document.getElementById('manual-absence-modal'); if (modal) modal.classList.add('hidden'); } async function submitManualAbsence(event) { if (event) event.preventDefault(); const idVal = document.getElementById('manual-absence-id')?.value.trim(); const fio = document.getElementById('manual-absence-fio-input')?.value.trim(); const deptVal = document.getElementById('manual-absence-dept')?.value.trim() || ''; const posVal = document.getElementById('manual-absence-pos')?.value.trim() || ''; const startDateVal = document.getElementById('manual-absence-start-date')?.value || null; const endDateVal = document.getElementById('manual-absence-end-date')?.value || null; const reasonSelect = document.getElementById('manual-absence-reason-select'); const errEl = document.getElementById('manual-absence-error'); if (!fio) { if (errEl) { errEl.innerText = 'Укажите ФИО сотрудника'; errEl.classList.remove('hidden'); } return; } const payload = { absence_type: activeAbsenceType, fio: fio, department: deptVal, position: posVal, date_start: startDateVal, date_end: endDateVal, reason: activeAbsenceType === 'LOCAL_TRIP' ? 'Местная командировка' : (reasonSelect ? reasonSelect.value : 'Иное') }; try { let res; if (idVal) { // Редактирование res = await fetch(`/api/v1/manual-absences/${idVal}`, { method: 'PUT', headers: { ...AuthManager.getAuthHeaders(), 'Content-Type': 'application/json' }, body: JSON.stringify({ id: parseInt(idVal), date_start: startDateVal, date_end: endDateVal }) }); } else { // Создание res = await fetch('/api/v1/manual-absences/', { method: 'POST', headers: { ...AuthManager.getAuthHeaders(), 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); } if (res.ok) { closeManualAbsenceModal(); if (typeof loadManualAbsencesView === 'function') { loadManualAbsencesView(activeAbsenceType); } } else { const data = await res.json(); if (errEl) { errEl.innerText = data.detail || 'Ошибка сохранения'; errEl.classList.remove('hidden'); } } } catch (e) { if (errEl) { errEl.innerText = 'Сетевая ошибка при сохранении'; errEl.classList.remove('hidden'); } } } // Универсальный автокомплит сотрудников let searchTimeout = null; function setupStaffAutocomplete(inputEl, suggestionsBoxId) { const box = document.getElementById(suggestionsBoxId); if (!inputEl || !box) return; inputEl.addEventListener('input', function() { const val = this.value.trim(); clearTimeout(searchTimeout); if (val.length < 2) { box.classList.add('hidden'); box.innerHTML = ''; return; } searchTimeout = setTimeout(async () => { try { const res = await fetch(`/api/v1/manual-absences/staff-autocomplete?q=${encodeURIComponent(val)}`); if (!res.ok) return; const items = await res.json(); if (items.length === 0) { box.classList.add('hidden'); return; } box.innerHTML = items.map(it => `
${escapeHtml(it.fio)} ${escapeHtml(it.department)} · ${escapeHtml(it.position)}
`).join(''); box.classList.remove('hidden'); } catch (e) { console.error(e); } }, 200); }); document.addEventListener('click', (e) => { if (!inputEl.contains(e.target) && !box.contains(e.target)) { box.classList.add('hidden'); } }); } function selectStaffSuggestion(fio, dept, pos, targetInputId, boxId) { const targetFioEl = document.getElementById(targetInputId); if (targetFioEl) targetFioEl.value = fio; if (targetInputId === 'rw-fio') { const rwDept = document.getElementById('rw-dept'); if (rwDept && dept && dept !== '—') rwDept.value = dept; } else if (targetInputId === 'manual-absence-fio-input') { const deptInput = document.getElementById('manual-absence-dept'); const posInput = document.getElementById('manual-absence-pos'); if (deptInput) deptInput.value = dept; if (posInput) posInput.value = pos; } const box = document.getElementById(boxId); if (box) box.classList.add('hidden'); } document.addEventListener('DOMContentLoaded', () => { loadAbsenceReasons(); });