// documents.js - Работа с документами для согласования let documentTypes = []; 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 initializeDocumentForm() { const form = document.getElementById('create-document-form'); if (form) { form.addEventListener('submit', createDocumentTask); } // Инициализация даты по умолчанию const today = new Date(); const todayStr = today.toISOString().split('T')[0]; const dateInput = document.getElementById('document-date'); if (dateInput) { dateInput.value = todayStr; } loadDocumentTypes(); } async function createDocumentTask() { console.log('📝 Создание документа...'); // Собираем данные формы const formData = new FormData(); // Обязательное поле - только название const title = document.getElementById('doc-title').value.trim(); if (!title) { showNotification('Название документа обязательно', 'error'); return; } formData.append('title', title); formData.append('description', document.getElementById('doc-description').value); formData.append('dueDate', document.getElementById('doc-due-date').value); // Тип документа - опционально const documentTypeSelect = document.getElementById('doc-type'); if (documentTypeSelect && documentTypeSelect.value) { formData.append('documentTypeId', documentTypeSelect.value); } // Остальные поля - опционально formData.append('documentNumber', document.getElementById('doc-number')?.value || ''); formData.append('documentDate', document.getElementById('doc-date')?.value || ''); formData.append('pagesCount', document.getElementById('doc-pages')?.value || ''); const urgencySelect = document.getElementById('doc-urgency'); if (urgencySelect) { formData.append('urgencyLevel', urgencySelect.value); } formData.append('comment', document.getElementById('doc-comment')?.value || ''); // Добавляем файлы (опционально) const fileInput = document.getElementById('doc-files'); if (fileInput && fileInput.files) { for (let i = 0; i < fileInput.files.length; i++) { formData.append('files', fileInput.files[i]); } } // Показываем индикатор загрузки const submitBtn = document.querySelector('#new-doc-form button[type="submit"]'); const originalText = submitBtn.innerHTML; submitBtn.innerHTML = ' Создание...'; submitBtn.disabled = true; try { console.log('📤 Отправка запроса на создание документа...'); const response = await fetch('/api/documents', { method: 'POST', body: formData }); const result = await response.json(); if (response.ok) { console.log('✅ Документ создан успешно:', result); // Показываем сообщение об успехе showNotification('Документ успешно создан и отправлен на согласование', 'success'); // Закрываем модальное окно const modal = bootstrap.Modal.getInstance(document.getElementById('newDocModal')); if (modal) modal.hide(); // Очищаем форму const form = document.getElementById('new-doc-form'); if (form) form.reset(); // Обновляем список документов await loadMyDocuments(); } else { console.error('❌ Ошибка создания документа:', result); showNotification(`Ошибка: ${result.error || 'Неизвестная ошибка'}`, 'error'); } } catch (error) { console.error('❌ Ошибка сети при создании документа:', error); showNotification('Ошибка сети при создании документа', 'error'); } finally { // Восстанавливаем кнопку if (submitBtn) { submitBtn.innerHTML = originalText; submitBtn.disabled = false; } } } function updateDocumentFileList() { const fileInput = document.getElementById('document-files'); const fileList = document.getElementById('document-file-list'); const files = fileInput.files; if (files.length === 0) { fileList.innerHTML = ''; return; } let html = ''; html += `

Общий размер: ${(totalSize / 1024 / 1024).toFixed(2)} MB / 300 MB

`; fileList.innerHTML = html; } // Функции для работы с документами async function loadMyDocuments() { try { const response = await fetch('/api/documents/my'); const documents = await response.json(); renderMyDocuments(documents); } catch (error) { console.error('Ошибка загрузки документов:', error); } } async function loadSecretaryDocuments() { try { const response = await fetch('/api/documents/secretary'); const documents = await response.json(); renderSecretaryDocuments(documents); } catch (error) { console.error('Ошибка загрузки документов секретаря:', error); } } function renderMyDocuments(documents) { console.log('📄 Рендеринг документов:', documents); const container = document.getElementById('my-docs-list'); if (!documents || !Array.isArray(documents)) { console.error('❌ documents не является массивом:', documents); container.innerHTML = `

Ошибка загрузки документов

`; return; } if (documents.length === 0) { container.innerHTML = `

У вас нет документов для согласования

`; return; } container.innerHTML = documents.map(doc => { // Определяем статус let statusClass = 'status-pending'; let statusText = 'На согласовании'; if (doc.assignment_status === 'completed' || doc.assignment_status === 'approved') { statusClass = 'status-completed'; statusText = 'Согласован'; } else if (doc.assignment_status === 'refused') { statusClass = 'status-cancelled'; statusText = 'Отказано'; } else if (doc.closed_at) { statusClass = 'status-cancelled'; statusText = 'Отозван'; } // Форматируем дату const createdDate = new Date(doc.created_at).toLocaleDateString('ru-RU'); const dueDate = doc.due_date ? new Date(doc.due_date).toLocaleDateString('ru-RU') : 'Не указана'; // Определяем уровень срочности let urgencyBadge = ''; if (doc.urgency_level === 'urgent') { urgencyBadge = 'Срочно'; } else if (doc.urgency_level === 'very_urgent') { urgencyBadge = 'Очень срочно'; } // Проверяем наличие типа документа const documentType = doc.document_type_name || 'Не указан'; return `

${doc.title.replace('Документ: ', '')}

${statusText}
Тип: ${documentType}
${doc.document_number ? `
Номер: ${doc.document_number}
` : ''} ${doc.document_date ? `
Дата документа: ${new Date(doc.document_date).toLocaleDateString('ru-RU')}
` : ''}
Создан: ${createdDate}
Срок согласования: ${dueDate}
${doc.urgency_level && doc.urgency_level !== 'normal' ? `
Срочность: ${urgencyBadge}
` : ''} ${doc.assignee_name ? `
Согласующий: ${doc.assignee_name}
` : ''} ${doc.refusal_reason ? `
Причина отказа: ${doc.refusal_reason}
` : ''}
${doc.description ? `

${doc.description}

` : ''} ${doc.comment ? `
Комментарий:

${doc.comment}

` : ''} ${doc.files && doc.files.length > 0 ? `
Файлы:
` : ''} ${!doc.closed_at && doc.assignment_status !== 'completed' && doc.assignment_status !== 'approved' && doc.assignment_status !== 'refused' ? `
` : ''}
`; }).join(''); } function formatFileSize(bytes) { if (bytes === 0) return '0 Б'; const k = 1024; const sizes = ['Б', 'КБ', 'МБ', 'ГБ']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; } // Исправьте функцию loadMyDocuments: async function loadMyDocuments() { console.log('📥 Загрузка моих документов...'); try { const response = await fetch('/api/documents/my'); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const documents = await response.json(); console.log('✅ Получены документы:', documents); renderMyDocuments(documents); } catch (error) { console.error('❌ Ошибка загрузки документов:', error); showNotification('Ошибка загрузки документов', 'error'); const container = document.getElementById('my-docs-list'); container.innerHTML = `

Ошибка загрузки документов: ${error.message}

`; } } function renderSecretaryDocuments(documents) { const container = document.getElementById('secretary-documents-list'); if (!container) return; if (documents.length === 0) { container.innerHTML = '
Нет документов для согласования
'; return; } container.innerHTML = documents.map(doc => `
Документ №${doc.document_number || doc.id} ${doc.title} ${getDocumentStatusText(doc.status)} ${doc.urgency_level === 'urgent' ? 'Срочно' : ''} ${doc.urgency_level === 'very_urgent' ? 'Очень срочно' : ''}
От: ${doc.creator_name} Создан: ${formatDateTime(doc.created_at)} ${doc.due_date ? `Срок: ${formatDateTime(doc.due_date)}` : ''}

Тип: ${doc.document_type_name || 'Не указан'}

Номер: ${doc.document_number || 'Не указан'}

Дата документа: ${doc.document_date ? formatDate(doc.document_date) : 'Не указана'}

Количество страниц: ${doc.pages_count || 'Не указано'}

${doc.comment ? `

Комментарий автора: ${doc.comment}

` : ''}
${doc.files && doc.files.length > 0 ? ` Файлы:
${doc.files.map(file => renderFileIcon(file)).join('')}
` : 'Файлы: нет файлов'}
${doc.status === 'assigned' ? ` ` : ''} ${doc.status === 'in_progress' ? `
` : ''} ${doc.status === 'approved' ? `
` : ''} ${doc.status === 'received' ? `
` : ''} ${doc.status === 'refused' ? `

Причина отказа: ${doc.refusal_reason}

` : ''}
`).join(''); } function getDocumentStatusClass(status) { switch(status) { case 'assigned': return 'status-assigned'; case 'in_progress': return 'status-in-progress'; case 'approved': return 'status-approved'; case 'received': return 'status-received'; case 'signed': return 'status-signed'; case 'refused': return 'status-refused'; case 'cancelled': return 'status-cancelled'; default: return 'status-assigned'; } } function getDocumentStatusText(status) { switch(status) { case 'assigned': return 'Назначена'; case 'in_progress': return 'В работе'; case 'approved': return 'Согласован'; case 'received': return 'Получен'; case 'signed': return 'Подписан'; case 'refused': return 'Отказано'; case 'cancelled': return 'Отозвано'; default: return status; } } function formatDate(dateString) { if (!dateString) return ''; return new Date(dateString).toLocaleDateString('ru-RU'); } // Модальные окна для секретаря function showApproveModal(documentId) { currentDocumentId = documentId; document.getElementById('approve-document-modal').style.display = 'block'; } function closeApproveModal() { document.getElementById('approve-document-modal').style.display = 'none'; document.getElementById('approve-comment').value = ''; } function showReceiveModal(documentId) { currentDocumentId = documentId; document.getElementById('receive-document-modal').style.display = 'block'; } function closeReceiveModal() { document.getElementById('receive-document-modal').style.display = 'none'; document.getElementById('receive-comment').value = ''; } function showRefuseModal(documentId) { currentDocumentId = documentId; document.getElementById('refuse-document-modal').style.display = 'block'; } function closeRefuseModal() { document.getElementById('refuse-document-modal').style.display = 'none'; document.getElementById('refuse-reason').value = ''; } let currentDocumentId = null; // Функции для работы с API async function updateDocumentStatus(documentId, status, comment = '', refusalReason = '') { try { const response = await fetch(`/api/documents/${documentId}/status`, { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ status: status, comment: comment, refusalReason: refusalReason }) }); if (response.ok) { alert('Статус документа обновлен!'); // Закрываем модальные окна closeApproveModal(); closeReceiveModal(); closeRefuseModal(); // Обновляем список документов if (isSecretary()) { loadSecretaryDocuments(); } loadMyDocuments(); } else { const error = await response.json(); alert(error.error || 'Ошибка обновления статуса'); } } 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('Ошибка отзыва документа'); } } async function reworkDocument(documentId) { // Здесь можно открыть форму для повторной отправки alert('Функция исправления и повторной отправки будет реализована в следующей версии'); } async function downloadDocumentPackage(documentId) { try { const response = await fetch(`/api/documents/${documentId}/package`); if (response.ok) { const blob = await response.blob(); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `document_${documentId}_package.zip`; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); document.body.removeChild(a); } else { const error = await response.json(); alert(error.error || 'Ошибка скачивания пакета документов'); } } catch (error) { console.error('Ошибка:', error); alert('Ошибка скачивания пакета документов'); } } function isSecretary() { return currentUser && currentUser.groups && currentUser.groups.includes('Секретарь'); } function showDocumentSection(sectionName) { // Скрываем все секции document.querySelectorAll('.document-section').forEach(section => { section.style.display = 'none'; }); // Показываем выбранную секцию const targetSection = document.getElementById(`${sectionName}-section`); if (targetSection) { targetSection.style.display = 'block'; } // Загружаем данные для секции if (sectionName === 'my-documents') { loadMyDocuments(); } else if (sectionName === 'secretary-documents' && isSecretary()) { loadSecretaryDocuments(); } } // Инициализация при загрузке страницы document.addEventListener('DOMContentLoaded', function() { if (window.location.pathname === '/doc') { initializeDocumentForm(); // Показываем соответствующие секции if (isSecretary()) { document.getElementById('secretary-tab').style.display = 'block'; } // По умолчанию показываем создание документа showDocumentSection('create-document'); } });