// search.js - адаптированный под Bootstrap (function () { // Состояние приложения let state = { filters: { period: 'today', dateFrom: null, dateTo: null, status: 'all', deliveryType: 'all', signatureType: 'all', senderName: '', patientName: '', documentNumber: '' }, results: [], pagination: { currentPage: 1, totalPages: 1, totalItems: 0, itemsPerPage: 10 }, loading: false, serverConfig: { ip: null, port: null } }; // Bootstrap модальное окно let statusModal = null; // DOM элементы const elements = { periodSelect: document.getElementById('periodSelect'), customDateRange: document.getElementById('customDateRange'), dateFrom: document.getElementById('dateFrom'), dateTo: document.getElementById('dateTo'), statusSelect: document.getElementById('statusSelect'), deliveryTypeSelect: document.getElementById('deliveryTypeSelect'), signatureTypeSelect: document.getElementById('signatureTypeSelect'), senderInput: document.getElementById('senderInput'), patientInput: document.getElementById('patientInput'), documentNumberInput: document.getElementById('documentNumberInput'), advancedToggle: document.getElementById('advancedToggle'), advancedFilters: document.getElementById('advancedFilters'), advancedIcon: document.getElementById('advancedIcon'), searchBtn: document.getElementById('searchBtn'), clearFiltersBtn: document.getElementById('clearFiltersBtn'), tableBody: document.getElementById('tableBody'), emptyState: document.getElementById('emptyState'), resultsStats: document.getElementById('resultsStats'), pagination: document.getElementById('pagination'), paginationInfo: document.getElementById('paginationInfo'), prevPage: document.getElementById('prevPage'), nextPage: document.getElementById('nextPage') }; // Инициализация async function init() { // Инициализация Bootstrap модального окна const modalElement = document.getElementById('statusHistoryModal'); if (modalElement) { statusModal = new bootstrap.Modal(modalElement); } await loadServerConfig(); await loadFiltersFromStorage(); setupEventListeners(); setDefaultDates(); // Автоматически выполняем поиск при загрузке setTimeout(() => performSearch(), 100); } // Загрузка конфигурации сервера async function loadServerConfig() { return new Promise((resolve) => { chrome.storage.local.get(['serverIp', 'serverPort'], (result) => { if (result.serverIp && result.serverPort) { state.serverConfig.ip = result.serverIp; state.serverConfig.port = result.serverPort; } else { showAlert('Сервер не настроен. Перейдите в настройки расширения.', 'warning'); } resolve(); }); }); } // Загрузка фильтров из storage async function loadFiltersFromStorage() { return new Promise((resolve) => { chrome.storage.local.get(['advancedSearchFilters'], (result) => { if (result.advancedSearchFilters) { state.filters = { ...state.filters, ...result.advancedSearchFilters }; // Применяем фильтры к элементам управления elements.periodSelect.value = state.filters.period; elements.statusSelect.value = state.filters.status; elements.deliveryTypeSelect.value = state.filters.deliveryType; elements.signatureTypeSelect.value = state.filters.signatureType; elements.senderInput.value = state.filters.senderName || ''; elements.patientInput.value = state.filters.patientName || ''; elements.documentNumberInput.value = state.filters.documentNumber || ''; if (state.filters.dateFrom) elements.dateFrom.value = state.filters.dateFrom; if (state.filters.dateTo) elements.dateTo.value = state.filters.dateTo; // Если период произвольный - показываем поля дат if (state.filters.period === 'custom') { elements.customDateRange.style.display = 'block'; } } resolve(); }); }); } // Сохранение фильтров в storage function saveFiltersToStorage() { chrome.storage.local.set({ advancedSearchFilters: state.filters }); } // Установка обработчиков событий function setupEventListeners() { // Период elements.periodSelect.addEventListener('change', handlePeriodChange); // Фильтры elements.statusSelect.addEventListener('change', updateFilters); elements.deliveryTypeSelect.addEventListener('change', updateFilters); elements.signatureTypeSelect.addEventListener('change', updateFilters); elements.senderInput.addEventListener('input', debounce(updateFilters, 500)); elements.patientInput.addEventListener('input', debounce(updateFilters, 500)); elements.documentNumberInput.addEventListener('input', debounce(updateFilters, 500)); // Произвольные даты elements.dateFrom.addEventListener('change', updateFilters); elements.dateTo.addEventListener('change', updateFilters); // Кнопки elements.searchBtn.addEventListener('click', performSearch); elements.clearFiltersBtn.addEventListener('click', clearFilters); // Расширенные фильтры elements.advancedToggle.addEventListener('click', toggleAdvancedFilters); // Пагинация elements.prevPage.addEventListener('click', () => changePage(-1)); elements.nextPage.addEventListener('click', () => changePage(1)); } // Установка дат по умолчанию function setDefaultDates() { if (!state.filters.dateFrom || !state.filters.dateTo) { const today = new Date(); const yesterday = new Date(today); yesterday.setDate(yesterday.getDate() - 1); elements.dateTo.value = formatDate(today); elements.dateFrom.value = formatDate(yesterday); state.filters.dateFrom = elements.dateFrom.value; state.filters.dateTo = elements.dateTo.value; } } // Форматирование даты для input function formatDate(date) { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); return `${year}-${month}-${day}`; } // Обработчик изменения периода function handlePeriodChange() { const period = elements.periodSelect.value; if (period === 'custom') { elements.customDateRange.style.display = 'block'; elements.advancedFilters.style.display = 'block'; elements.advancedIcon.className = 'bi bi-chevron-up me-1'; } else { elements.customDateRange.style.display = 'none'; updateDateRangeFromPeriod(period); } updateFilters(); } // Обновление диапазона дат на основе периода function updateDateRangeFromPeriod(period) { const today = new Date(); let from = new Date(today); switch (period) { case 'today': break; case '3days': from.setDate(from.getDate() - 3); break; case '7days': from.setDate(from.getDate() - 7); break; case '30days': from.setDate(from.getDate() - 30); break; case '90days': from.setDate(from.getDate() - 90); break; } elements.dateFrom.value = formatDate(from); elements.dateTo.value = formatDate(today); state.filters.dateFrom = elements.dateFrom.value; state.filters.dateTo = elements.dateTo.value; } // Обновление состояния фильтров function updateFilters() { state.filters = { period: elements.periodSelect.value, dateFrom: elements.dateFrom.value, dateTo: elements.dateTo.value, status: elements.statusSelect.value, deliveryType: elements.deliveryTypeSelect.value, signatureType: elements.signatureTypeSelect.value, senderName: elements.senderInput.value.trim(), patientName: elements.patientInput.value.trim(), documentNumber: elements.documentNumberInput.value.trim() }; saveFiltersToStorage(); } // Переключение расширенных фильтров function toggleAdvancedFilters() { const isVisible = elements.advancedFilters.style.display !== 'none'; if (isVisible) { elements.advancedFilters.style.display = 'none'; elements.advancedIcon.className = 'bi bi-chevron-down me-1'; if (state.filters.period !== 'custom') { elements.customDateRange.style.display = 'none'; } } else { elements.advancedFilters.style.display = 'block'; elements.advancedIcon.className = 'bi bi-chevron-up me-1'; if (state.filters.period === 'custom') { elements.customDateRange.style.display = 'block'; } } } // Сброс фильтров function clearFilters() { elements.periodSelect.value = 'today'; elements.customDateRange.style.display = 'none'; elements.statusSelect.value = 'all'; elements.deliveryTypeSelect.value = 'all'; elements.signatureTypeSelect.value = 'all'; elements.senderInput.value = ''; elements.patientInput.value = ''; elements.documentNumberInput.value = ''; setDefaultDates(); updateFilters(); elements.advancedFilters.style.display = 'none'; elements.advancedIcon.className = 'bi bi-chevron-down me-1'; // Выполняем поиск после сброса performSearch(); } // Выполнение поиска async function performSearch() { if (!validateServerConfig()) { return; } setLoading(true); try { updateFilters(); const response = await sendMessageToBackground('advancedSearch', { filters: state.filters }); if (response.success) { state.results = response.data || []; state.pagination.totalItems = state.results.length; state.pagination.totalPages = Math.ceil(state.pagination.totalItems / state.pagination.itemsPerPage); state.pagination.currentPage = 1; renderResults(); } else { showAlert(response.message || 'Ошибка при поиске', 'danger'); } } catch (error) { console.error('Search error:', error); showAlert('Ошибка при выполнении поиска', 'danger'); } finally { setLoading(false); } } // Определение типа подписания function getSignatureType(item) { return item.esiaAuth === true ? 'esia' : 'mchd'; } function getSignatureTypeText(item) { return getSignatureType(item) === 'esia' ? 'УКЭП для ЕСИА' : 'МЧД для других'; } function getSignatureTypeClass(item) { return getSignatureType(item) === 'esia' ? 'bg-primary-subtle text-primary' : 'bg-secondary-subtle text-secondary'; } // Отправка сообщения в background function sendMessageToBackground(action, data) { return new Promise((resolve) => { chrome.runtime.sendMessage({ action, data }, (response) => { resolve(response || { success: false, message: 'Нет ответа от background' }); }); }); } // Валидация конфигурации сервера function validateServerConfig() { if (!state.serverConfig.ip || !state.serverConfig.port) { showAlert('Сервер не настроен. Перейдите в настройки расширения.', 'warning'); return false; } return true; } // Установка состояния загрузки function setLoading(isLoading) { state.loading = isLoading; if (isLoading) { elements.searchBtn.disabled = true; elements.tableBody.innerHTML = `
Нет истории статусов
'; if (statusModal) statusModal.show(); return; } // Разделяем статусы на общие и по получателям const commonStatuses = statuses.filter(s => s.idPatientMis === null); const recipientStatuses = statuses.filter(s => s.idPatientMis !== null); // Группируем статусы по получателям const statusesByRecipient = {}; recipientStatuses.forEach(status => { if (status.patient) { const patientName = status.patient.name || 'Неизвестный получатель'; if (!statusesByRecipient[patientName]) { statusesByRecipient[patientName] = []; } statusesByRecipient[patientName].push(status); } }); // Сортируем получателей по имени const sortedRecipients = Object.keys(statusesByRecipient).sort(); // Функция для создания HTML статуса const createStatusHtml = (status) => { return `