// doc.js - Согласование документов document.addEventListener('DOMContentLoaded', function() { if (window.location.pathname === '/doc') { loadDocumentTypes(); setupDocumentForm(); loadMyDocuments(); setupDocumentFilters(); } }); let documentTypes = []; let allDocuments = []; let filteredDocuments = []; async function loadDocumentTypes() { try { const response = await fetch('/api/document-types'); documentTypes = await response.json(); populateDocumentTypeSelect(); } catch (error) { console.error('Ошибка загрузки типов документов:', error); } } function populateDocumentTypeSelect() { const select = document.getElementById('document-type'); if (!select) return; select.innerHTML = ''; documentTypes.forEach(type => { const option = document.createElement('option'); option.value = type.id; option.textContent = type.name; select.appendChild(option); }); } function setupDocumentForm() { const form = document.getElementById('create-document-form'); if (!form) return; form.addEventListener('submit', createDocument); // Устанавливаем текущую дату для даты документа const today = new Date().toISOString().split('T')[0]; const documentDateInput = document.getElementById('document-date'); if (documentDateInput) { documentDateInput.value = today; } // Устанавливаем дату выполнения (по умолчанию через 7 дней) const dueDate = new Date(); dueDate.setDate(dueDate.getDate() + 7); const dueDateInput = document.getElementById('due-date'); if (dueDateInput) { dueDateInput.value = dueDate.toISOString().split('T')[0]; } } async function createDocument(event) { event.preventDefault(); if (!currentUser) { alert('Требуется аутентификация'); return; } const formData = new FormData(); // Собираем данные формы const title = document.getElementById('title').value; const description = document.getElementById('description').value; const documentTypeId = document.getElementById('document-type').value; const documentNumber = document.getElementById('document-number').value; const documentDate = document.getElementById('document-date').value; const pagesCount = document.getElementById('pages-count').value; const urgencyLevel = document.getElementById('urgency-level').value; const dueDate = document.getElementById('due-date').value; const comment = document.getElementById('comment').value; if (!title || title.trim() === '') { alert('Название документа обязательно'); return; } formData.append('title', title); formData.append('description', description || ''); formData.append('dueDate', dueDate || ''); formData.append('documentTypeId', documentTypeId || ''); formData.append('documentNumber', documentNumber || ''); formData.append('documentDate', documentDate || ''); formData.append('pagesCount', pagesCount || ''); formData.append('urgencyLevel', urgencyLevel || 'normal'); formData.append('comment', comment || ''); // Добавляем файлы const files = document.getElementById('files').files; for (let i = 0; i < files.length; i++) { formData.append('files', files[i]); } try { const response = await fetch('/api/documents', { method: 'POST', body: formData }); if (response.ok) { const result = await response.json(); alert(result.message || 'Документ успешно создан и отправлен на согласование!'); // Сбрасываем форму document.getElementById('create-document-form').reset(); document.getElementById('file-list').innerHTML = ''; // Загружаем мои документы loadMyDocuments(); // Возвращаемся к списку документов showDocumentSection('my-documents'); } else { const error = await response.json(); alert(error.error || 'Ошибка создания документа'); } } catch (error) { console.error('Ошибка:', error); alert('Ошибка создания документа'); } } async function loadMyDocuments() { try { const response = await fetch('/api/documents/my'); allDocuments = await response.json(); filteredDocuments = [...allDocuments]; renderMyDocuments(); } catch (error) { console.error('Ошибка загрузки документов:', error); document.getElementById('my-documents-list').innerHTML = '
Ошибка загрузки документов
'; } } async function loadSecretaryDocuments() { try { const response = await fetch('/api/documents/secretary'); allDocuments = await response.json(); filteredDocuments = [...allDocuments]; renderSecretaryDocuments(); } catch (error) { console.error('Ошибка загрузки документов секретаря:', error); document.getElementById('secretary-documents-list').innerHTML = '
Ошибка загрузки документов
'; } } function renderMyDocuments() { const container = document.getElementById('my-documents-list'); if (filteredDocuments.length === 0) { container.innerHTML = '
Нет документов
'; return; } container.innerHTML = filteredDocuments.map(doc => { const status = getDocumentStatus(doc); const statusClass = getDocumentStatusClass(status); const isCancelled = doc.status === 'cancelled'; const isClosed = doc.closed_at !== null; const timeLeftInfo = getDocumentTimeLeftInfo(doc); return `
Док. №${doc.document_number || doc.id} ${doc.title} ${isCancelled ? 'Отозван' : ''} ${isClosed ? 'Завершен' : ''} ${timeLeftInfo ? `${timeLeftInfo.text}` : ''} ${status}
${!isCancelled && !isClosed ? ` ` : ''} ${doc.files && doc.files.length > 0 ? ` ` : ''}
Тип документа: ${doc.document_type_name || 'Не указан'}
${doc.description ? `
Описание: ${doc.description}
` : ''}
Статус согласования: ${doc.assignment_status || 'Не назначен'}
${doc.refusal_reason ? `
Причина отказа: ${doc.refusal_reason}
` : ''} ${doc.comment ? `
Комментарий создателя: ${doc.comment}
` : ''}
Дата документа: ${formatDate(doc.document_date)}
Количество страниц: ${doc.pages_count || 'Не указано'}
Срочность: ${getUrgencyText(doc.urgency_level)}
Срок согласования: ${formatDate(doc.due_date)}
Файлы: ${doc.files && doc.files.length > 0 ? renderDocumentFiles(doc.files) : 'нет файлов' }
Создан: ${formatDateTime(doc.created_at)} ${doc.closed_at ? `
Завершен: ${formatDateTime(doc.closed_at)}` : ''}
`; }).join(''); } function renderSecretaryDocuments() { const container = document.getElementById('secretary-documents-list'); if (filteredDocuments.length === 0) { container.innerHTML = '
Нет документов для согласования
'; return; } container.innerHTML = filteredDocuments.map(doc => { const status = getDocumentStatus(doc); const statusClass = getDocumentStatusClass(status); const timeLeftInfo = getDocumentTimeLeftInfo(doc); return `
Док. №${doc.document_number || doc.id} ${doc.title} ${timeLeftInfo ? `${timeLeftInfo.text}` : ''} ${status} От: ${doc.creator_name}
${doc.files && doc.files.length > 0 ? ` ` : ''}
Тип документа: ${doc.document_type_name || 'Не указан'}
${doc.description ? `
Описание: ${doc.description}
` : ''} ${doc.comment ? `
Комментарий создателя: ${doc.comment}
` : ''} ${doc.refusal_reason ? `
Ранее отказано: ${doc.refusal_reason}
` : ''}
Дата документа: ${formatDate(doc.document_date)}
Номер документа: ${doc.document_number || 'Не указан'}
Количество страниц: ${doc.pages_count || 'Не указано'}
Срочность: ${getUrgencyText(doc.urgency_level)}
Срок согласования: ${formatDate(doc.due_date)}
Файлы: ${doc.files && doc.files.length > 0 ? renderDocumentFiles(doc.files) : 'нет файлов' }
Создан: ${formatDateTime(doc.created_at)}
`; }).join(''); } function renderDocumentFiles(files) { return `
${files.map(file => `
${file.original_name} ${formatFileSize(file.file_size)}
`).join('')}
`; } function getDocumentStatus(doc) { if (doc.status === 'cancelled') return 'Отозван'; if (doc.closed_at) return 'Завершен'; switch(doc.assignment_status) { case 'pre_approved': return 'Предварительно согласован'; case 'approved': return 'Согласован'; case 'refused': return 'Отказано'; case 'received': return 'Получен оригинал'; case 'signed': return 'Подписан'; case 'assigned': return 'На согласовании'; default: return 'Создан'; } } function getDocumentStatusClass(status) { switch(status) { case 'Согласован': case 'Подписан': case 'Получен оригинал': return 'status-approved'; case 'Предварительно согласован': return 'status-pre-approved'; case 'Отказано': return 'status-refused'; case 'Отозван': return 'status-cancelled'; case 'Завершен': return 'status-closed'; default: return 'status-pending'; } } function getUrgencyText(urgency) { switch(urgency) { case 'very_urgent': return 'Очень срочно'; case 'urgent': return 'Срочно'; default: return 'Обычная'; } } function getDocumentTimeLeftInfo(doc) { if (!doc.due_date || doc.closed_at) return null; const dueDate = new Date(doc.due_date); const now = new Date(); const timeLeft = dueDate.getTime() - now.getTime(); const daysLeft = Math.floor(timeLeft / (24 * 60 * 60 * 1000)); if (daysLeft <= 0) return null; if (daysLeft <= 1) { return { text: `Менее 1 дня`, class: 'deadline-urgent' }; } else if (daysLeft <= 3) { return { text: `${daysLeft} дня`, class: 'deadline-warning' }; } return null; } function formatDate(dateString) { if (!dateString) return 'Не указана'; const date = new Date(dateString); return date.toLocaleDateString('ru-RU'); } function formatFileSize(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } function downloadFile(fileId) { window.open(`/api/files/${fileId}/download`, '_blank'); } async function downloadDocumentPackage(documentId) { try { const response = await fetch(`/api/documents/${documentId}/package`); const result = await response.json(); if (result.success && result.downloadUrl) { window.open(result.downloadUrl, '_blank'); } else { alert(result.message || 'Функция создания пакета документов будет реализована в следующей версии'); } } catch (error) { console.error('Ошибка скачивания пакета:', error); alert('Ошибка скачивания пакета документов'); } } async function cancelDocument(documentId) { if (!confirm('Вы уверены, что хотите отозвать этот документ?')) { return; } try { const response = await fetch(`/api/documents/${documentId}/cancel`, { method: 'POST' }); if (response.ok) { alert('Документ отозван!'); loadMyDocuments(); } else { const error = await response.json(); alert(error.error || 'Ошибка отзыва документа'); } } catch (error) { console.error('Ошибка:', error); alert('Ошибка отзыва документа'); } } function openPreApproveModal(documentId) { document.getElementById('approve-modal-type').value = 'pre_approve'; document.getElementById('approve-modal-document-id').value = documentId; document.getElementById('approve-comment').value = ''; document.getElementById('refusal-reason').style.display = 'none'; document.getElementById('approve-modal').style.display = 'block'; } function openApproveModal(documentId) { document.getElementById('approve-modal-type').value = 'approve'; document.getElementById('approve-modal-document-id').value = documentId; document.getElementById('approve-comment').value = ''; document.getElementById('refusal-reason').style.display = 'none'; document.getElementById('approve-modal').style.display = 'block'; } function openRefuseModal(documentId) { document.getElementById('approve-modal-type').value = 'refuse'; document.getElementById('approve-modal-document-id').value = documentId; document.getElementById('approve-comment').value = ''; document.getElementById('refusal-reason').style.display = 'block'; document.getElementById('approve-modal').style.display = 'block'; } function closeApproveModal() { document.getElementById('approve-modal').style.display = 'none'; document.getElementById('approve-comment').value = ''; document.getElementById('refusal-reason-text').value = ''; } async function submitDocumentStatus(event) { event.preventDefault(); const documentId = document.getElementById('approve-modal-document-id').value; const actionType = document.getElementById('approve-modal-type').value; const comment = document.getElementById('approve-comment').value; const refusalReason = document.getElementById('refusal-reason-text').value; let status = ''; switch(actionType) { case 'pre_approve': status = 'pre_approved'; break; case 'approve': status = 'approved'; break; case 'refuse': status = 'refused'; break; default: return; } try { const response = await fetch(`/api/documents/${documentId}/status`, { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ status, comment, refusalReason: actionType === 'refuse' ? refusalReason : null }) }); if (response.ok) { alert('Статус документа обновлен!'); closeApproveModal(); loadSecretaryDocuments(); } else { const error = await response.json(); alert(error.error || 'Ошибка обновления статуса'); } } catch (error) { console.error('Ошибка:', error); alert('Ошибка обновления статуса документа'); } } function setupDocumentFilters() { const searchInput = document.getElementById('search-documents'); const statusFilter = document.getElementById('document-status-filter'); if (searchInput) { searchInput.addEventListener('input', filterDocuments); } if (statusFilter) { statusFilter.addEventListener('change', filterDocuments); } } function filterDocuments() { const search = document.getElementById('search-documents')?.value.toLowerCase() || ''; const statusFilter = document.getElementById('document-status-filter')?.value || 'all'; filteredDocuments = allDocuments.filter(doc => { // Поиск по названию и номеру const matchesSearch = doc.title.toLowerCase().includes(search) || (doc.document_number && doc.document_number.toLowerCase().includes(search)) || (doc.description && doc.description.toLowerCase().includes(search)); if (!matchesSearch) return false; // Фильтрация по статусу if (statusFilter === 'all') return true; const docStatus = getDocumentStatus(doc); return docStatus === statusFilter; }); // Определяем, какую секцию рендерить const activeSection = document.querySelector('.document-section.active'); if (activeSection && activeSection.id === 'my-documents-section') { renderMyDocuments(); } else if (activeSection && activeSection.id === 'secretary-documents-section') { renderSecretaryDocuments(); } } function showDocumentSection(sectionName) { // Скрыть все секции document.querySelectorAll('.document-section').forEach(section => { section.classList.remove('active'); }); // Скрыть все кнопки document.querySelectorAll('.nav-btn').forEach(btn => { btn.classList.remove('active'); }); // Показать выбранную секцию document.getElementById(sectionName + '-section').classList.add('active'); // Активировать соответствующую кнопку const btn = document.querySelector(`.nav-btn[onclick*="${sectionName}"]`); if (btn) btn.classList.add('active'); // Загрузить данные для секции if (sectionName === 'my-documents') { loadMyDocuments(); } else if (sectionName === 'secretary-documents') { loadSecretaryDocuments(); } }