brief-rags-bench/static/js/ui/questions-list.ui.js

167 lines
5.0 KiB
JavaScript

/**
* Questions List UI
*
* UI компонент для списка вопросов.
*/
import appState from '../state/appState.js'
import { escapeHtml, formatTime, pluralize } from '../utils/format.utils.js'
import { setElementText, setElementHTML } from '../utils/dom.utils.js'
/**
* Проверить наличие аннотаций в секции документов
* @param {object} docsSection - Секция документов
* @returns {boolean} True если есть аннотации
*/
export function hasAnnotationsInDocs(docsSection) {
if (!docsSection) return false
// Check research documents
if (docsSection.research) {
for (const docIndex in docsSection.research) {
const doc = docsSection.research[docIndex]
if (doc.issues?.length > 0 || doc.comment) {
return true
}
}
}
// Check analytical_hub documents
if (docsSection.analytical_hub) {
for (const docIndex in docsSection.analytical_hub) {
const doc = docsSection.analytical_hub[docIndex]
if (doc.issues?.length > 0 || doc.comment) {
return true
}
}
}
return false
}
/**
* Отрендерить список вопросов
*/
export function render() {
const container = document.getElementById('questions-list')
const countElement = document.getElementById('questions-count')
if (!container) {
console.error('Questions list container not found')
return
}
const env = appState.getCurrentEnv()
const response = env.currentResponse
if (!response || !response.answers || response.answers.length === 0) {
setElementHTML(container, `
<div class="empty-state">
<div class="empty-state-icon">
<span class="material-icons" style="font-size: inherit;">question_answer</span>
</div>
<div class="empty-state-text">Нет данных</div>
<div class="empty-state-subtext">Отправьте запрос к RAG бэкенду</div>
</div>
`)
if (countElement) {
setElementText(countElement, '0 вопросов')
}
return
}
// Update count
if (countElement) {
const count = response.answers.length
const text = `${count} ${pluralize(count, 'вопрос', 'вопроса', 'вопросов')}`
setElementText(countElement, text)
}
// Render questions
const html = response.answers.map((answer, index) => {
const isActive = index === env.currentAnswerIndex
const annotation = env.annotations[index]
// Check for annotations in body sections
const hasBodyAnnotations = annotation && (
annotation.overall?.comment ||
annotation.body_research?.issues?.length > 0 ||
annotation.body_analytical_hub?.issues?.length > 0
)
// Check for annotations in documents
const hasDocAnnotations = annotation && (
hasAnnotationsInDocs(annotation.docs_from_vectorstore) ||
hasAnnotationsInDocs(annotation.docs_to_llm)
)
const hasAnyAnnotations = hasBodyAnnotations || hasDocAnnotations
// Get rating indicator
const rating = annotation?.overall?.rating
let ratingIndicator = ''
if (rating === 'correct') {
ratingIndicator = '<span class="material-icons" style="font-size: 18px; color: #4caf50;">check_circle</span>'
} else if (rating === 'partial') {
ratingIndicator = '<span class="material-icons" style="font-size: 18px; color: #ff9800;">error</span>'
} else if (rating === 'incorrect') {
ratingIndicator = '<span class="material-icons" style="font-size: 18px; color: #f44336;">cancel</span>'
}
// Get annotation bookmark indicator (separate from rating)
const annotationIndicator = hasAnyAnnotations
? '<span class="material-icons color-warning" style="font-size: 18px;">bookmark</span>'
: ''
return `
<div class="card card-clickable question-item ${isActive ? 'active' : ''}"
data-index="${index}"
onclick="window.selectAnswer(${index})">
<div class="card-content">
<div class="question-item-header">
<div class="text-overline">#${index + 1}</div>
<div style="display: flex; gap: 4px;">
${ratingIndicator}
${annotationIndicator}
</div>
</div>
<div class="question-text">${escapeHtml(answer.question)}</div>
<div class="question-meta">
<span>${formatTime(answer.processing_time_sec)}</span>
</div>
</div>
</div>
`
}).join('')
setElementHTML(container, html)
}
/**
* Выбрать ответ по индексу
* @param {number} index - Индекс ответа
* @param {Function} onSelect - Callback при выборе
*/
export function selectAnswer(index, onSelect) {
const env = appState.getCurrentEnv()
env.currentAnswerIndex = index
// Re-render questions list to update active state
render()
// Call callback if provided
if (typeof onSelect === 'function') {
onSelect(index)
}
}
// Export as default object
export default {
render,
selectAnswer,
hasAnnotationsInDocs
}