441 lines
12 KiB
JavaScript
441 lines
12 KiB
JavaScript
/**
|
|
* Brief Bench - Main Entry Point
|
|
*
|
|
* Главная точка входа приложения.
|
|
* Импортирует все модули и инициализирует приложение.
|
|
*/
|
|
|
|
// Import services
|
|
import * as authService from './services/auth.service.js'
|
|
import settingsService from './services/settings.service.js'
|
|
import queryService from './services/query.service.js'
|
|
|
|
// Import UI components
|
|
import authUI from './ui/auth.ui.js'
|
|
import loadingUI from './ui/loading.ui.js'
|
|
import settingsUI from './ui/settings.ui.js'
|
|
import queryBuilderUI from './ui/query-builder.ui.js'
|
|
import questionsListUI from './ui/questions-list.ui.js'
|
|
import answerViewerUI from './ui/answer-viewer.ui.js'
|
|
import annotationsUI from './ui/annotations.ui.js'
|
|
|
|
// Import state
|
|
import appState from './state/appState.js'
|
|
|
|
// Import utils
|
|
import { showToast } from './utils/dom.utils.js'
|
|
import { downloadJSON } from './utils/file.utils.js'
|
|
import { ENVIRONMENTS } from './config.js'
|
|
|
|
// ============================================
|
|
// Global Functions (for HTML onclick handlers)
|
|
// ============================================
|
|
|
|
/**
|
|
* Expose functions to window for HTML onclick handlers
|
|
*/
|
|
window.selectAnswer = function(index) {
|
|
questionsListUI.selectAnswer(index, (idx) => {
|
|
answerViewerUI.render(idx, annotationsUI.loadForAnswer)
|
|
})
|
|
}
|
|
|
|
window.toggleExpansion = function(id) {
|
|
answerViewerUI.toggleExpansion(id)
|
|
}
|
|
|
|
window.switchTab = function(tabButton, tabId) {
|
|
queryBuilderUI.switchTab(tabButton, tabId)
|
|
}
|
|
|
|
// ============================================
|
|
// Environment Management
|
|
// ============================================
|
|
|
|
/**
|
|
* Switch to a different environment
|
|
* @param {string} env - Environment name (ift/psi/prod)
|
|
*/
|
|
function switchEnvironment(env) {
|
|
// Save current environment data
|
|
const currentEnv = appState.getCurrentEnvironment()
|
|
appState.saveEnvironmentToStorage(currentEnv)
|
|
|
|
// Switch environment
|
|
appState.setCurrentEnvironment(env)
|
|
appState.updateSettings({ activeEnvironment: env })
|
|
|
|
// Update UI
|
|
updateEnvironmentTabs()
|
|
|
|
// Reload content for new environment
|
|
const newEnv = appState.getCurrentEnv()
|
|
if (newEnv.currentResponse) {
|
|
questionsListUI.render()
|
|
answerViewerUI.render(newEnv.currentAnswerIndex, annotationsUI.loadForAnswer)
|
|
} else {
|
|
queryBuilderUI.show()
|
|
}
|
|
|
|
console.log('Switched to environment:', env)
|
|
}
|
|
|
|
/**
|
|
* Update environment tabs visual state
|
|
*/
|
|
function updateEnvironmentTabs() {
|
|
const currentEnv = appState.getCurrentEnvironment()
|
|
|
|
document.querySelectorAll('.env-tab').forEach(tab => {
|
|
if (tab.dataset.env === currentEnv) {
|
|
tab.classList.add('active')
|
|
} else {
|
|
tab.classList.remove('active')
|
|
}
|
|
})
|
|
}
|
|
|
|
// ============================================
|
|
// Event Listeners Setup
|
|
// ============================================
|
|
|
|
/**
|
|
* Setup all event listeners
|
|
*/
|
|
function setupEventListeners() {
|
|
// Prevent double-binding
|
|
if (window._eventListenersSetup) {
|
|
return
|
|
}
|
|
window._eventListenersSetup = true
|
|
|
|
// Environment tabs
|
|
document.querySelectorAll('.env-tab').forEach(tab => {
|
|
tab.addEventListener('click', (e) => {
|
|
const env = e.target.dataset.env
|
|
if (env) {
|
|
switchEnvironment(env)
|
|
}
|
|
})
|
|
})
|
|
|
|
// Settings environment selector change
|
|
const settingsEnvSelector = document.getElementById('settings-env-selector')
|
|
if (settingsEnvSelector) {
|
|
settingsEnvSelector.addEventListener('change', () => {
|
|
// Save current environment settings first
|
|
const currentSettings = settingsUI.read()
|
|
if (currentSettings) {
|
|
appState.setSettings(currentSettings)
|
|
}
|
|
// Load new environment settings
|
|
settingsUI.populate()
|
|
})
|
|
}
|
|
|
|
// App bar buttons
|
|
const menuBtn = document.getElementById('menu-btn')
|
|
if (menuBtn) {
|
|
menuBtn.addEventListener('click', toggleDrawer)
|
|
}
|
|
|
|
const newQueryBtn = document.getElementById('new-query-btn')
|
|
if (newQueryBtn) {
|
|
newQueryBtn.addEventListener('click', () => queryBuilderUI.show())
|
|
}
|
|
|
|
const exportBtn = document.getElementById('export-btn')
|
|
if (exportBtn) {
|
|
exportBtn.addEventListener('click', window.exportAnalysis)
|
|
}
|
|
|
|
const importBtn = document.getElementById('import-btn')
|
|
if (importBtn) {
|
|
importBtn.addEventListener('click', window.importAnalysis)
|
|
}
|
|
|
|
// Drawer buttons
|
|
const clearAllBtn = document.getElementById('clear-all-btn')
|
|
if (clearAllBtn) {
|
|
clearAllBtn.addEventListener('click', clearAll)
|
|
}
|
|
|
|
// Query builder file operations
|
|
const loadRequestBtn = document.getElementById('load-request-btn')
|
|
if (loadRequestBtn) {
|
|
loadRequestBtn.addEventListener('click', loadRequestFromFile)
|
|
}
|
|
|
|
const loadResponseBtn = document.getElementById('load-response-btn')
|
|
if (loadResponseBtn) {
|
|
loadResponseBtn.addEventListener('click', loadResponseFromFile)
|
|
}
|
|
|
|
const validateJsonBtn = document.getElementById('validate-json-btn')
|
|
if (validateJsonBtn) {
|
|
validateJsonBtn.addEventListener('click', () => queryBuilderUI.validateJSONMode())
|
|
}
|
|
|
|
// Setup UI component listeners
|
|
authUI.setupListeners()
|
|
settingsUI.setupListeners()
|
|
queryBuilderUI.setupListeners(() => {
|
|
// Callback after successful query
|
|
questionsListUI.render()
|
|
answerViewerUI.render(0, annotationsUI.loadForAnswer)
|
|
})
|
|
|
|
// Tab switching
|
|
document.querySelectorAll('.tab').forEach(tab => {
|
|
tab.addEventListener('click', (e) => {
|
|
const tabId = e.target.dataset.tab
|
|
if (tabId) {
|
|
queryBuilderUI.switchTab(e.target, tabId)
|
|
}
|
|
})
|
|
})
|
|
|
|
// Close dialog on overlay click
|
|
const settingsDialog = document.getElementById('settings-dialog')
|
|
if (settingsDialog) {
|
|
settingsDialog.addEventListener('click', (e) => {
|
|
if (e.target.id === 'settings-dialog') {
|
|
settingsUI.close()
|
|
}
|
|
})
|
|
}
|
|
|
|
console.log('Event listeners setup complete')
|
|
}
|
|
|
|
// ============================================
|
|
// UI Utility Functions
|
|
// ============================================
|
|
|
|
/**
|
|
* Toggle drawer visibility (mobile)
|
|
*/
|
|
function toggleDrawer() {
|
|
const drawer = document.getElementById('drawer')
|
|
if (drawer) {
|
|
drawer.classList.toggle('collapsed')
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clear all data and reload page
|
|
*/
|
|
function clearAll() {
|
|
if (confirm('Очистить все данные и обновить страницу? Несохраненные изменения будут потеряны.')) {
|
|
window.location.reload()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Load request from JSON file
|
|
*/
|
|
function loadRequestFromFile() {
|
|
const input = document.createElement('input')
|
|
input.type = 'file'
|
|
input.accept = 'application/json'
|
|
|
|
input.onchange = async (e) => {
|
|
const file = e.target.files[0]
|
|
if (!file) return
|
|
|
|
try {
|
|
const requestBody = await queryService.loadRequestFromFile(file)
|
|
|
|
// Load into textarea
|
|
const jsonTextarea = document.getElementById('json-textarea')
|
|
if (jsonTextarea) {
|
|
jsonTextarea.value = JSON.stringify(requestBody, null, 2)
|
|
queryBuilderUI.validateJSONMode()
|
|
}
|
|
|
|
showToast(`Загружен запрос: ${requestBody.length} вопросов`, 'success')
|
|
} catch (error) {
|
|
showToast(`Ошибка загрузки запроса: ${error.message}`, 'error')
|
|
}
|
|
}
|
|
|
|
input.click()
|
|
}
|
|
|
|
/**
|
|
* Load response from JSON file
|
|
*/
|
|
function loadResponseFromFile() {
|
|
const input = document.createElement('input')
|
|
input.type = 'file'
|
|
input.accept = 'application/json'
|
|
|
|
input.onchange = async (e) => {
|
|
const file = e.target.files[0]
|
|
if (!file) return
|
|
|
|
try {
|
|
const currentEnvKey = appState.getCurrentEnvironment()
|
|
await queryService.loadResponseFromFile(file, currentEnvKey)
|
|
|
|
// Update UI
|
|
questionsListUI.render()
|
|
answerViewerUI.render(0, annotationsUI.loadForAnswer)
|
|
|
|
showToast(`Загружен ответ`, 'success')
|
|
} catch (error) {
|
|
showToast(`Ошибка загрузки ответа: ${error.message}`, 'error')
|
|
}
|
|
}
|
|
|
|
input.click()
|
|
}
|
|
|
|
// ============================================
|
|
// Export/Import Functions
|
|
// ============================================
|
|
|
|
/**
|
|
* Export current analysis to JSON file
|
|
*/
|
|
window.exportAnalysis = function() {
|
|
const env = appState.getCurrentEnv()
|
|
const currentEnvKey = appState.getCurrentEnvironment()
|
|
|
|
if (!env.currentResponse) {
|
|
showToast('Нет данных для экспорта', 'warning')
|
|
return
|
|
}
|
|
|
|
const exportData = {
|
|
environment: currentEnvKey,
|
|
request_id: env.requestId,
|
|
timestamp: env.requestTimestamp,
|
|
request: env.currentRequest,
|
|
response: env.currentResponse,
|
|
annotations: env.annotations
|
|
}
|
|
|
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').substring(0, 19)
|
|
const filename = `brief-bench-analysis-${currentEnvKey}-${timestamp}.json`
|
|
|
|
downloadJSON(exportData, filename)
|
|
showToast(`Анализ экспортирован: ${filename}`, 'success')
|
|
}
|
|
|
|
/**
|
|
* Import analysis from JSON file
|
|
*/
|
|
window.importAnalysis = function() {
|
|
const input = document.createElement('input')
|
|
input.type = 'file'
|
|
input.accept = 'application/json'
|
|
|
|
input.onchange = async (e) => {
|
|
const file = e.target.files[0]
|
|
if (!file) return
|
|
|
|
try {
|
|
loadingUI.show('Импорт анализа...')
|
|
|
|
const text = await file.text()
|
|
const data = JSON.parse(text)
|
|
|
|
// Validate data
|
|
if (!data.response || !data.response.answers) {
|
|
throw new Error('Некорректный формат файла анализа')
|
|
}
|
|
|
|
const currentEnvKey = appState.getCurrentEnvironment()
|
|
const env = appState.getEnvironment(currentEnvKey)
|
|
|
|
// Load data
|
|
env.currentRequest = data.request || []
|
|
env.currentResponse = data.response
|
|
env.requestId = data.request_id || 'imported'
|
|
env.requestTimestamp = data.timestamp || new Date().toISOString()
|
|
env.annotations = data.annotations || {}
|
|
env.currentAnswerIndex = 0
|
|
|
|
// Save to localStorage
|
|
appState.saveEnvironmentToStorage(currentEnvKey)
|
|
|
|
// Update UI
|
|
questionsListUI.render()
|
|
answerViewerUI.render(0, annotationsUI.loadForAnswer)
|
|
|
|
loadingUI.hide()
|
|
showToast('Анализ успешно импортирован', 'success')
|
|
} catch (error) {
|
|
loadingUI.hide()
|
|
console.error('Failed to import analysis:', error)
|
|
showToast(`Ошибка импорта: ${error.message}`, 'error')
|
|
}
|
|
}
|
|
|
|
input.click()
|
|
}
|
|
|
|
// ============================================
|
|
// Application Initialization
|
|
// ============================================
|
|
|
|
/**
|
|
* Initialize application
|
|
*/
|
|
async function initApp() {
|
|
try {
|
|
// Load settings from server
|
|
await settingsService.loadFromServer()
|
|
|
|
// Set current environment from settings
|
|
const activeEnv = appState.settings?.activeEnvironment || 'ift'
|
|
appState.setCurrentEnvironment(activeEnv)
|
|
|
|
// Load saved data for each environment from localStorage
|
|
ENVIRONMENTS.forEach(env => {
|
|
appState.loadEnvironmentFromStorage(env)
|
|
})
|
|
|
|
// Setup event listeners
|
|
setupEventListeners()
|
|
|
|
// Update environment tabs
|
|
updateEnvironmentTabs()
|
|
|
|
// Show query builder by default
|
|
queryBuilderUI.show()
|
|
|
|
console.log('Brief Bench initialized - Environment:', appState.getCurrentEnvironment())
|
|
} catch (error) {
|
|
console.error('Failed to initialize app:', error)
|
|
showToast('Ошибка инициализации приложения', 'error')
|
|
}
|
|
}
|
|
|
|
// ============================================
|
|
// Application Entry Point
|
|
// ============================================
|
|
|
|
/**
|
|
* Initialize app on DOM load
|
|
*/
|
|
document.addEventListener('DOMContentLoaded', async () => {
|
|
console.log('Brief Bench starting...')
|
|
|
|
// Setup event listeners first (including login/logout)
|
|
setupEventListeners()
|
|
|
|
// Check authentication
|
|
const isAuthenticated = await authService.checkAuth()
|
|
|
|
if (isAuthenticated) {
|
|
// User is authenticated, initialize app
|
|
authUI.hideLoginScreen()
|
|
await initApp()
|
|
} else {
|
|
// User is not authenticated, show login screen
|
|
authUI.showLoginScreen()
|
|
}
|
|
})
|