add plan
This commit is contained in:
parent
da2a67bc91
commit
6e29b2e6df
|
|
@ -0,0 +1,816 @@
|
||||||
|
# План рефакторинга app.js на ES6 модули
|
||||||
|
|
||||||
|
## Текущее состояние
|
||||||
|
|
||||||
|
- **Размер файла**: 1671 строка
|
||||||
|
- **Проблемы**:
|
||||||
|
- Монолитная структура затрудняет поддержку
|
||||||
|
- Все функции в глобальной области видимости
|
||||||
|
- Сложно тестировать отдельные компоненты
|
||||||
|
- Повторное использование кода затруднено
|
||||||
|
|
||||||
|
## Целевая архитектура
|
||||||
|
|
||||||
|
```
|
||||||
|
static/
|
||||||
|
├── index.html
|
||||||
|
├── styles.css
|
||||||
|
├── js/
|
||||||
|
│ ├── main.js # Точка входа, инициализация
|
||||||
|
│ ├── config.js # Константы и конфигурация
|
||||||
|
│ ├── state/
|
||||||
|
│ │ └── appState.js # Глобальное состояние приложения
|
||||||
|
│ ├── services/
|
||||||
|
│ │ ├── api-client.js # Существующий API клиент (переместить)
|
||||||
|
│ │ ├── auth.service.js # Аутентификация
|
||||||
|
│ │ ├── settings.service.js # Управление настройками
|
||||||
|
│ │ └── query.service.js # Запросы к RAG
|
||||||
|
│ ├── ui/
|
||||||
|
│ │ ├── auth.ui.js # UI авторизации
|
||||||
|
│ │ ├── settings.ui.js # Диалог настроек
|
||||||
|
│ │ ├── query-builder.ui.js # Построитель запросов
|
||||||
|
│ │ ├── answer-viewer.ui.js # Просмотр ответов
|
||||||
|
│ │ ├── questions-list.ui.js # Список вопросов
|
||||||
|
│ │ ├── annotations.ui.js # Аннотации
|
||||||
|
│ │ └── loading.ui.js # Индикаторы загрузки
|
||||||
|
│ ├── utils/
|
||||||
|
│ │ ├── dom.utils.js # DOM манипуляции
|
||||||
|
│ │ ├── format.utils.js # Форматирование (время, текст)
|
||||||
|
│ │ ├── file.utils.js # Работа с файлами
|
||||||
|
│ │ └── validation.utils.js # Валидация данных
|
||||||
|
│ └── data/
|
||||||
|
│ ├── storage.js # LocalStorage обертка
|
||||||
|
│ └── defaults.js # Дефолтные настройки (из settings.js)
|
||||||
|
└── settings.js # Удалить после рефакторинга
|
||||||
|
```
|
||||||
|
|
||||||
|
## Этапы рефакторинга
|
||||||
|
|
||||||
|
### Этап 1: Подготовка (день 1)
|
||||||
|
|
||||||
|
**Цель**: Настроить окружение для ES6 модулей
|
||||||
|
|
||||||
|
#### 1.1. Обновить index.html
|
||||||
|
```html
|
||||||
|
<!-- Заменить обычные скрипты на модули -->
|
||||||
|
<script type="module" src="js/main.js"></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 1.2. Создать структуру папок
|
||||||
|
```bash
|
||||||
|
mkdir static/js
|
||||||
|
mkdir static/js/state
|
||||||
|
mkdir static/js/services
|
||||||
|
mkdir static/js/ui
|
||||||
|
mkdir static/js/utils
|
||||||
|
mkdir static/js/data
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 1.3. Настроить конфигурацию
|
||||||
|
- Создать `js/config.js` с константами
|
||||||
|
- Определить экспорты/импорты
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Этап 2: Утилиты и вспомогательные функции (день 1-2)
|
||||||
|
|
||||||
|
**Цель**: Вынести независимые функции
|
||||||
|
|
||||||
|
#### 2.1. `utils/format.utils.js`
|
||||||
|
**Функции** (из app.js строки 150-188):
|
||||||
|
- `generateUUID()`
|
||||||
|
- `formatTime(seconds)`
|
||||||
|
- `formatTimestamp(isoString)`
|
||||||
|
- `isTableText(text)`
|
||||||
|
- `parseTextTable(text)`
|
||||||
|
- `escapeHtml(text)`
|
||||||
|
|
||||||
|
**Экспорт**:
|
||||||
|
```javascript
|
||||||
|
export {
|
||||||
|
generateUUID,
|
||||||
|
formatTime,
|
||||||
|
formatTimestamp,
|
||||||
|
isTableText,
|
||||||
|
parseTextTable,
|
||||||
|
escapeHtml
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2.2. `utils/file.utils.js`
|
||||||
|
**Функции** (строки 262-273, 984-1132):
|
||||||
|
- `downloadJSON(data, filename)`
|
||||||
|
- `loadFileAsJSON(file)` - новая функция для загрузки
|
||||||
|
- `loadFileAsText(file)` - новая функция
|
||||||
|
|
||||||
|
**Экспорт**:
|
||||||
|
```javascript
|
||||||
|
export {
|
||||||
|
downloadJSON,
|
||||||
|
loadFileAsJSON,
|
||||||
|
loadFileAsText
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2.3. `utils/validation.utils.js`
|
||||||
|
**Функции** (строки 823-860):
|
||||||
|
- `validateJSON(jsonString)`
|
||||||
|
- `validateLoginFormat(login)`
|
||||||
|
|
||||||
|
**Экспорт**:
|
||||||
|
```javascript
|
||||||
|
export {
|
||||||
|
validateJSON,
|
||||||
|
validateLoginFormat
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2.4. `utils/dom.utils.js`
|
||||||
|
**Функции**:
|
||||||
|
- `showElement(id)`
|
||||||
|
- `hideElement(id)`
|
||||||
|
- `toggleElement(id)`
|
||||||
|
- `setElementText(id, text)`
|
||||||
|
- `showToast(message, type)` (строка 277-281)
|
||||||
|
|
||||||
|
**Экспорт**:
|
||||||
|
```javascript
|
||||||
|
export {
|
||||||
|
showElement,
|
||||||
|
hideElement,
|
||||||
|
toggleElement,
|
||||||
|
setElementText,
|
||||||
|
showToast
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Этап 3: State Management (день 2)
|
||||||
|
|
||||||
|
**Цель**: Централизовать управление состоянием
|
||||||
|
|
||||||
|
#### 3.1. `state/appState.js`
|
||||||
|
**Содержимое** (строки 10-42):
|
||||||
|
```javascript
|
||||||
|
class AppState {
|
||||||
|
constructor() {
|
||||||
|
this.settings = { /* ... */ }
|
||||||
|
this.currentEnvironment = 'ift'
|
||||||
|
this.environments = {
|
||||||
|
ift: { /* ... */ },
|
||||||
|
psi: { /* ... */ },
|
||||||
|
prod: { /* ... */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Геттеры
|
||||||
|
getCurrentEnv() { /* ... */ }
|
||||||
|
getCurrentEnvSettings() { /* ... */ }
|
||||||
|
|
||||||
|
// Сеттеры
|
||||||
|
setCurrentEnvironment(env) { /* ... */ }
|
||||||
|
updateSettings(settings) { /* ... */ }
|
||||||
|
|
||||||
|
// Persistence
|
||||||
|
saveToLocalStorage() { /* ... */ }
|
||||||
|
loadFromLocalStorage() { /* ... */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new AppState()
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3.2. `data/storage.js`
|
||||||
|
**Функции**:
|
||||||
|
- `saveEnvironmentData(env, data)`
|
||||||
|
- `loadEnvironmentData(env)`
|
||||||
|
- `clearEnvironmentData(env)`
|
||||||
|
|
||||||
|
**Экспорт**:
|
||||||
|
```javascript
|
||||||
|
export {
|
||||||
|
saveEnvironmentData,
|
||||||
|
loadEnvironmentData,
|
||||||
|
clearEnvironmentData
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3.3. `data/defaults.js`
|
||||||
|
**Перенести из settings.js** (строки 1-70):
|
||||||
|
```javascript
|
||||||
|
export const defaultSettings = {
|
||||||
|
activeEnvironment: 'ift',
|
||||||
|
environments: { /* ... */ }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Этап 4: Services (день 3)
|
||||||
|
|
||||||
|
**Цель**: Бизнес-логика и API взаимодействие
|
||||||
|
|
||||||
|
#### 4.1. `services/api-client.js`
|
||||||
|
**Действие**: Переместить существующий `api-client.js` в папку `services/`
|
||||||
|
|
||||||
|
**Обновить**:
|
||||||
|
```javascript
|
||||||
|
class BriefBenchAPI {
|
||||||
|
// ... существующий код
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new BriefBenchAPI()
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4.2. `services/auth.service.js`
|
||||||
|
**Функции** (строки 60-140):
|
||||||
|
- `checkAuth()`
|
||||||
|
- `login(loginString)`
|
||||||
|
- `logout()`
|
||||||
|
- `isAuthenticated()`
|
||||||
|
|
||||||
|
**Экспорт**:
|
||||||
|
```javascript
|
||||||
|
import api from './api-client.js'
|
||||||
|
|
||||||
|
export class AuthService {
|
||||||
|
async checkAuth() { /* ... */ }
|
||||||
|
async login(loginString) { /* ... */ }
|
||||||
|
logout() { /* ... */ }
|
||||||
|
isAuthenticated() { /* ... */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new AuthService()
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4.3. `services/settings.service.js`
|
||||||
|
**Функции** (строки 290-357):
|
||||||
|
- `loadSettingsFromServer()`
|
||||||
|
- `saveSettingsToServer(settings)`
|
||||||
|
- `extractEnvironmentSettings(envSettings)`
|
||||||
|
- `resetToDefaults()`
|
||||||
|
|
||||||
|
**Экспорт**:
|
||||||
|
```javascript
|
||||||
|
import api from './api-client.js'
|
||||||
|
import appState from '../state/appState.js'
|
||||||
|
|
||||||
|
export class SettingsService {
|
||||||
|
async loadFromServer() { /* ... */ }
|
||||||
|
async saveToServer(settings) { /* ... */ }
|
||||||
|
extractEnvSettings(envSettings) { /* ... */ }
|
||||||
|
resetToDefaults() { /* ... */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new SettingsService()
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4.4. `services/query.service.js`
|
||||||
|
**Функции** (строки 861-1063):
|
||||||
|
- `buildRequestBody()`
|
||||||
|
- `sendQuery(environment, apiMode, requestBody)`
|
||||||
|
- `extractQuestions()`
|
||||||
|
- `loadRequestFromFile()`
|
||||||
|
- `loadResponseFromFile()`
|
||||||
|
|
||||||
|
**Экспорт**:
|
||||||
|
```javascript
|
||||||
|
import api from './api-client.js'
|
||||||
|
import appState from '../state/appState.js'
|
||||||
|
|
||||||
|
export class QueryService {
|
||||||
|
buildRequestBody() { /* ... */ }
|
||||||
|
async sendQuery(env, apiMode, body) { /* ... */ }
|
||||||
|
extractQuestions() { /* ... */ }
|
||||||
|
async loadRequestFromFile() { /* ... */ }
|
||||||
|
async loadResponseFromFile() { /* ... */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new QueryService()
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Этап 5: UI Components (день 4-5)
|
||||||
|
|
||||||
|
**Цель**: Разделить UI логику по компонентам
|
||||||
|
|
||||||
|
#### 5.1. `ui/auth.ui.js`
|
||||||
|
**Функции** (строки 77-132):
|
||||||
|
- `showLoginScreen()`
|
||||||
|
- `hideLoginScreen()`
|
||||||
|
- `setupLoginListeners()`
|
||||||
|
- `handleLoginSubmit()`
|
||||||
|
|
||||||
|
**Экспорт**:
|
||||||
|
```javascript
|
||||||
|
import authService from '../services/auth.service.js'
|
||||||
|
|
||||||
|
export class AuthUI {
|
||||||
|
showLoginScreen() { /* ... */ }
|
||||||
|
hideLoginScreen() { /* ... */ }
|
||||||
|
setupListeners() { /* ... */ }
|
||||||
|
async handleLoginSubmit() { /* ... */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new AuthUI()
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5.2. `ui/loading.ui.js`
|
||||||
|
**Функции** (строки 1137-1145):
|
||||||
|
- `showLoading(message)`
|
||||||
|
- `hideLoading()`
|
||||||
|
|
||||||
|
**Экспорт**:
|
||||||
|
```javascript
|
||||||
|
export class LoadingUI {
|
||||||
|
show(message) { /* ... */ }
|
||||||
|
hide() { /* ... */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new LoadingUI()
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5.3. `ui/settings.ui.js`
|
||||||
|
**Функции** (строки 362-813):
|
||||||
|
- `openSettingsDialog()`
|
||||||
|
- `closeSettingsDialog()`
|
||||||
|
- `populateSettingsDialog()`
|
||||||
|
- `readSettingsFromDialog()`
|
||||||
|
- `toggleBackendSettings(show)`
|
||||||
|
- `saveSettings()`
|
||||||
|
- `resetSettings()`
|
||||||
|
- `exportSettings()`
|
||||||
|
- `importSettings()`
|
||||||
|
|
||||||
|
**Экспорт**:
|
||||||
|
```javascript
|
||||||
|
import settingsService from '../services/settings.service.js'
|
||||||
|
import appState from '../state/appState.js'
|
||||||
|
|
||||||
|
export class SettingsUI {
|
||||||
|
open() { /* ... */ }
|
||||||
|
close() { /* ... */ }
|
||||||
|
populate() { /* ... */ }
|
||||||
|
read() { /* ... */ }
|
||||||
|
toggleBackendSettings(show) { /* ... */ }
|
||||||
|
async save() { /* ... */ }
|
||||||
|
async reset() { /* ... */ }
|
||||||
|
export() { /* ... */ }
|
||||||
|
async import() { /* ... */ }
|
||||||
|
setupListeners() { /* ... */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new SettingsUI()
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5.4. `ui/query-builder.ui.js`
|
||||||
|
**Функции** (строки 643-883):
|
||||||
|
- `showQueryBuilder()`
|
||||||
|
- `switchQueryMode(mode)`
|
||||||
|
- `validateJSON()`
|
||||||
|
- `setupQueryBuilderListeners()`
|
||||||
|
|
||||||
|
**Экспорт**:
|
||||||
|
```javascript
|
||||||
|
import queryService from '../services/query.service.js'
|
||||||
|
|
||||||
|
export class QueryBuilderUI {
|
||||||
|
show() { /* ... */ }
|
||||||
|
switchMode(mode) { /* ... */ }
|
||||||
|
validateJSON() { /* ... */ }
|
||||||
|
setupListeners() { /* ... */ }
|
||||||
|
async handleSendQuery() { /* ... */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new QueryBuilderUI()
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5.5. `ui/questions-list.ui.js`
|
||||||
|
**Функции** (строки 1179-1273):
|
||||||
|
- `renderQuestionsList()`
|
||||||
|
- `selectAnswer(index)`
|
||||||
|
- `updateQuestionsCount()`
|
||||||
|
- `hasAnnotationsInDocs(docsSection)`
|
||||||
|
- `pluralize(count, one, few, many)`
|
||||||
|
|
||||||
|
**Экспорт**:
|
||||||
|
```javascript
|
||||||
|
import appState from '../state/appState.js'
|
||||||
|
|
||||||
|
export class QuestionsListUI {
|
||||||
|
render() { /* ... */ }
|
||||||
|
selectAnswer(index) { /* ... */ }
|
||||||
|
updateCount() { /* ... */ }
|
||||||
|
hasAnnotations(docsSection) { /* ... */ }
|
||||||
|
setupListeners() { /* ... */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new QuestionsListUI()
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5.6. `ui/answer-viewer.ui.js`
|
||||||
|
**Функции** (строки 1279-1443):
|
||||||
|
- `renderAnswer(index)`
|
||||||
|
- `renderAnswerBody(elementId, text)`
|
||||||
|
- `renderDocuments(containerId, docs, ...)`
|
||||||
|
- `toggleExpansion(id)`
|
||||||
|
- `switchTab(tabButton, tabId)`
|
||||||
|
|
||||||
|
**Экспорт**:
|
||||||
|
```javascript
|
||||||
|
import appState from '../state/appState.js'
|
||||||
|
import { formatTime, parseTextTable, escapeHtml } from '../utils/format.utils.js'
|
||||||
|
|
||||||
|
export class AnswerViewerUI {
|
||||||
|
render(index) { /* ... */ }
|
||||||
|
renderBody(elementId, text) { /* ... */ }
|
||||||
|
renderDocuments(containerId, docs, ...) { /* ... */ }
|
||||||
|
toggleExpansion(id) { /* ... */ }
|
||||||
|
switchTab(tabButton, tabId) { /* ... */ }
|
||||||
|
setupListeners() { /* ... */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new AnswerViewerUI()
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5.7. `ui/annotations.ui.js`
|
||||||
|
**Функции** (строки 1448-1615):
|
||||||
|
- `initAnnotationForAnswer(index)`
|
||||||
|
- `loadAnnotationsForAnswer(index)`
|
||||||
|
- `loadSectionAnnotation(section, data)`
|
||||||
|
- `loadDocumentAnnotations(section, subsection, docs)`
|
||||||
|
- `setupAnnotationListeners()`
|
||||||
|
- `updateCheckboxStyle(checkbox)`
|
||||||
|
- `saveAnnotationsDraft()`
|
||||||
|
|
||||||
|
**Экспорт**:
|
||||||
|
```javascript
|
||||||
|
import appState from '../state/appState.js'
|
||||||
|
import { saveEnvironmentData } from '../data/storage.js'
|
||||||
|
|
||||||
|
export class AnnotationsUI {
|
||||||
|
initForAnswer(index) { /* ... */ }
|
||||||
|
loadForAnswer(index) { /* ... */ }
|
||||||
|
loadSection(section, data) { /* ... */ }
|
||||||
|
loadDocuments(section, subsection, docs) { /* ... */ }
|
||||||
|
setupListeners() { /* ... */ }
|
||||||
|
saveDraft() { /* ... */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new AnnotationsUI()
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Этап 6: Main Entry Point (день 6)
|
||||||
|
|
||||||
|
**Цель**: Создать точку входа и инициализацию
|
||||||
|
|
||||||
|
#### 6.1. `js/main.js`
|
||||||
|
```javascript
|
||||||
|
// Импорты
|
||||||
|
import appState from './state/appState.js'
|
||||||
|
import authService from './services/auth.service.js'
|
||||||
|
import settingsService from './services/settings.service.js'
|
||||||
|
import authUI from './ui/auth.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'
|
||||||
|
|
||||||
|
// Инициализация приложения
|
||||||
|
async function initApp() {
|
||||||
|
// Load settings from server
|
||||||
|
await settingsService.loadFromServer()
|
||||||
|
appState.setCurrentEnvironment(appState.settings.activeEnvironment || 'ift')
|
||||||
|
|
||||||
|
// Load saved data for each environment
|
||||||
|
appState.loadFromLocalStorage()
|
||||||
|
|
||||||
|
// Setup all UI listeners
|
||||||
|
authUI.setupListeners()
|
||||||
|
settingsUI.setupListeners()
|
||||||
|
queryBuilderUI.setupListeners()
|
||||||
|
questionsListUI.setupListeners()
|
||||||
|
answerViewerUI.setupListeners()
|
||||||
|
annotationsUI.setupListeners()
|
||||||
|
|
||||||
|
// Setup environment tabs
|
||||||
|
setupEnvironmentTabs()
|
||||||
|
|
||||||
|
// Render initial state
|
||||||
|
updateUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup environment tabs
|
||||||
|
function setupEnvironmentTabs() {
|
||||||
|
const tabs = document.querySelectorAll('.env-tab')
|
||||||
|
tabs.forEach(tab => {
|
||||||
|
tab.addEventListener('click', () => {
|
||||||
|
switchEnvironment(tab.dataset.env)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch environment
|
||||||
|
function switchEnvironment(env) {
|
||||||
|
appState.setCurrentEnvironment(env)
|
||||||
|
updateEnvironmentTabs()
|
||||||
|
updateUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update environment tabs
|
||||||
|
function updateEnvironmentTabs() {
|
||||||
|
const tabs = document.querySelectorAll('.env-tab')
|
||||||
|
tabs.forEach(tab => {
|
||||||
|
if (tab.dataset.env === appState.currentEnvironment) {
|
||||||
|
tab.classList.add('active')
|
||||||
|
} else {
|
||||||
|
tab.classList.remove('active')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update UI based on current state
|
||||||
|
function updateUI() {
|
||||||
|
questionsListUI.render()
|
||||||
|
|
||||||
|
const env = appState.getCurrentEnv()
|
||||||
|
if (env.currentResponse && env.currentResponse.answers) {
|
||||||
|
answerViewerUI.render(env.currentAnswerIndex || 0)
|
||||||
|
} else {
|
||||||
|
queryBuilderUI.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entry point
|
||||||
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
|
const isAuthenticated = await authService.checkAuth()
|
||||||
|
|
||||||
|
if (isAuthenticated) {
|
||||||
|
await initApp()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 6.2. `js/config.js`
|
||||||
|
```javascript
|
||||||
|
// API Configuration
|
||||||
|
export const API_CONFIG = {
|
||||||
|
baseURL: '/api/v1',
|
||||||
|
timeout: 1800000 // 30 minutes
|
||||||
|
}
|
||||||
|
|
||||||
|
// UI Configuration
|
||||||
|
export const UI_CONFIG = {
|
||||||
|
defaultQueryMode: 'questions',
|
||||||
|
defaultWithDocs: true,
|
||||||
|
maxAnnotationLength: 5000
|
||||||
|
}
|
||||||
|
|
||||||
|
// Storage Keys
|
||||||
|
export const STORAGE_KEYS = {
|
||||||
|
token: 'briefBenchToken',
|
||||||
|
user: 'briefBenchUser',
|
||||||
|
settings: 'briefBenchSettings',
|
||||||
|
envData: (env) => `briefBenchData_${env}`,
|
||||||
|
annotations: 'briefBenchAnnotationsDraft'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
export const ENVIRONMENTS = ['ift', 'psi', 'prod']
|
||||||
|
export const API_MODES = ['bench', 'backend']
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Этап 7: Тестирование и оптимизация (день 7)
|
||||||
|
|
||||||
|
### 7.1. Ручное тестирование
|
||||||
|
- ✅ Авторизация работает
|
||||||
|
- ✅ Загрузка/сохранение настроек
|
||||||
|
- ✅ Отправка запросов (bench/backend)
|
||||||
|
- ✅ Отображение ответов
|
||||||
|
- ✅ Аннотации сохраняются
|
||||||
|
- ✅ Экспорт/импорт работает
|
||||||
|
- ✅ Переключение окружений
|
||||||
|
|
||||||
|
### 7.2. Проверка производительности
|
||||||
|
- Измерить время загрузки
|
||||||
|
- Проверить размер бандла
|
||||||
|
- Оптимизировать импорты
|
||||||
|
|
||||||
|
### 7.3. Очистка
|
||||||
|
- Удалить старый `app.js`
|
||||||
|
- Удалить `settings.js`
|
||||||
|
- Обновить `.gitignore` если нужно
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Миграционная стратегия
|
||||||
|
|
||||||
|
### Вариант 1: Постепенная миграция (РЕКОМЕНДУЕТСЯ)
|
||||||
|
|
||||||
|
**Подход**: Модули живут параллельно с монолитом
|
||||||
|
|
||||||
|
1. Создать папку `js/` с модулями
|
||||||
|
2. Оставить `app.js` работающим
|
||||||
|
3. Постепенно переносить функции
|
||||||
|
4. Тестировать после каждого этапа
|
||||||
|
5. Когда все готово - переключиться на `main.js`
|
||||||
|
|
||||||
|
**Преимущества**:
|
||||||
|
- Можно откатиться в любой момент
|
||||||
|
- Меньше риска
|
||||||
|
- Легче тестировать
|
||||||
|
|
||||||
|
**index.html во время миграции**:
|
||||||
|
```html
|
||||||
|
<!-- Старый код (работает) -->
|
||||||
|
<script src="settings.js"></script>
|
||||||
|
<script src="api-client.js"></script>
|
||||||
|
<script src="app.js"></script>
|
||||||
|
|
||||||
|
<!-- Новый код (тестируется) -->
|
||||||
|
<!-- <script type="module" src="js/main.js"></script> -->
|
||||||
|
```
|
||||||
|
|
||||||
|
### Вариант 2: Быстрая миграция
|
||||||
|
|
||||||
|
**Подход**: Переписать всё за раз
|
||||||
|
|
||||||
|
**НЕ РЕКОМЕНДУЕТСЯ** из-за высокого риска багов
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Чек-лист миграции
|
||||||
|
|
||||||
|
### Подготовка
|
||||||
|
- [ ] Создать ветку `feature/modularize-frontend`
|
||||||
|
- [ ] Сделать backup текущего app.js
|
||||||
|
- [ ] Создать структуру папок
|
||||||
|
|
||||||
|
### Этап 1: Утилиты
|
||||||
|
- [ ] Создать `utils/format.utils.js`
|
||||||
|
- [ ] Создать `utils/file.utils.js`
|
||||||
|
- [ ] Создать `utils/validation.utils.js`
|
||||||
|
- [ ] Создать `utils/dom.utils.js`
|
||||||
|
- [ ] Тесты: проверить что функции работают
|
||||||
|
|
||||||
|
### Этап 2: State
|
||||||
|
- [ ] Создать `state/appState.js`
|
||||||
|
- [ ] Создать `data/storage.js`
|
||||||
|
- [ ] Создать `data/defaults.js`
|
||||||
|
- [ ] Тесты: проверить геттеры/сеттеры
|
||||||
|
|
||||||
|
### Этап 3: Services
|
||||||
|
- [ ] Переместить `api-client.js` в `services/`
|
||||||
|
- [ ] Создать `services/auth.service.js`
|
||||||
|
- [ ] Создать `services/settings.service.js`
|
||||||
|
- [ ] Создать `services/query.service.js`
|
||||||
|
- [ ] Тесты: проверить API вызовы
|
||||||
|
|
||||||
|
### Этап 4: UI Components
|
||||||
|
- [ ] Создать `ui/auth.ui.js`
|
||||||
|
- [ ] Создать `ui/loading.ui.js`
|
||||||
|
- [ ] Создать `ui/settings.ui.js`
|
||||||
|
- [ ] Создать `ui/query-builder.ui.js`
|
||||||
|
- [ ] Создать `ui/questions-list.ui.js`
|
||||||
|
- [ ] Создать `ui/answer-viewer.ui.js`
|
||||||
|
- [ ] Создать `ui/annotations.ui.js`
|
||||||
|
- [ ] Тесты: проверить рендеринг
|
||||||
|
|
||||||
|
### Этап 5: Main
|
||||||
|
- [ ] Создать `js/main.js`
|
||||||
|
- [ ] Создать `js/config.js`
|
||||||
|
- [ ] Обновить `index.html`
|
||||||
|
- [ ] Тесты: полный цикл работы
|
||||||
|
|
||||||
|
### Этап 6: Очистка
|
||||||
|
- [ ] Удалить старый `app.js`
|
||||||
|
- [ ] Удалить `settings.js`
|
||||||
|
- [ ] Обновить документацию
|
||||||
|
- [ ] Code review
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Преимущества после рефакторинга
|
||||||
|
|
||||||
|
### Для разработки
|
||||||
|
- ✅ **Модульность**: Каждый файл отвечает за свою область
|
||||||
|
- ✅ **Тестируемость**: Легко покрыть модули unit-тестами
|
||||||
|
- ✅ **Читаемость**: Проще найти нужную функцию
|
||||||
|
- ✅ **Переиспользование**: Функции можно использовать в других проектах
|
||||||
|
|
||||||
|
### Для поддержки
|
||||||
|
- ✅ **Изоляция**: Баг в одном модуле не затронет другие
|
||||||
|
- ✅ **Масштабируемость**: Легко добавлять новые функции
|
||||||
|
- ✅ **Документация**: Каждый модуль самодокументируемый
|
||||||
|
- ✅ **Team work**: Разные разработчики могут работать над разными модулями
|
||||||
|
|
||||||
|
### Для производительности
|
||||||
|
- ✅ **Tree shaking**: Неиспользуемый код не попадет в бандл
|
||||||
|
- ✅ **Lazy loading**: Можно подгружать модули по требованию
|
||||||
|
- ✅ **Кеширование**: Браузер может кешировать отдельные модули
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Риски и митигация
|
||||||
|
|
||||||
|
### Риск 1: Поломка существующего функционала
|
||||||
|
**Митигация**: Постепенная миграция с тестированием после каждого этапа
|
||||||
|
|
||||||
|
### Риск 2: Увеличение времени загрузки (много файлов)
|
||||||
|
**Митигация**: Использовать bundler (Vite/Webpack) для production
|
||||||
|
|
||||||
|
### Риск 3: Проблемы совместимости браузеров
|
||||||
|
**Митигация**: ES6 модули поддерживаются всеми современными браузерами
|
||||||
|
|
||||||
|
### Риск 4: Сложность отладки
|
||||||
|
**Митигация**: Source maps и понятная структура папок
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Следующие шаги
|
||||||
|
|
||||||
|
1. **Обсудить план** с командой
|
||||||
|
2. **Выбрать стратегию миграции** (постепенная/быстрая)
|
||||||
|
3. **Создать ветку** для рефакторинга
|
||||||
|
4. **Начать с Этапа 1** (утилиты)
|
||||||
|
5. **Тестировать** после каждого этапа
|
||||||
|
6. **Code review** перед мержем
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Временные оценки
|
||||||
|
|
||||||
|
| Этап | Описание | Время |
|
||||||
|
|------|----------|-------|
|
||||||
|
| Этап 1 | Подготовка | 2 часа |
|
||||||
|
| Этап 2 | Утилиты | 3 часа |
|
||||||
|
| Этап 3 | State | 2 часа |
|
||||||
|
| Этап 4 | Services | 4 часа |
|
||||||
|
| Этап 5 | UI Components | 8 часов |
|
||||||
|
| Этап 6 | Main Entry | 2 часа |
|
||||||
|
| Этап 7 | Тестирование | 4 часа |
|
||||||
|
| **ИТОГО** | | **~25 часов** (3-4 рабочих дня) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Пример использования после рефакторинга
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Было (app.js, строка 900):
|
||||||
|
async function handleSendQuery() {
|
||||||
|
const envSettings = getCurrentEnvSettings()
|
||||||
|
const env = getCurrentEnv()
|
||||||
|
const apiMode = envSettings.apiMode || 'bench'
|
||||||
|
// ... 50 строк кода
|
||||||
|
}
|
||||||
|
|
||||||
|
// Стало (js/ui/query-builder.ui.js):
|
||||||
|
import queryService from '../services/query.service.js'
|
||||||
|
import appState from '../state/appState.js'
|
||||||
|
import loadingUI from './loading.ui.js'
|
||||||
|
|
||||||
|
export class QueryBuilderUI {
|
||||||
|
async handleSendQuery() {
|
||||||
|
const envSettings = appState.getCurrentEnvSettings()
|
||||||
|
const apiMode = envSettings.apiMode || 'bench'
|
||||||
|
|
||||||
|
loadingUI.show('Отправка запроса...')
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await queryService.sendQuery(
|
||||||
|
appState.currentEnvironment,
|
||||||
|
apiMode,
|
||||||
|
this.buildRequestBody()
|
||||||
|
)
|
||||||
|
|
||||||
|
// Update state
|
||||||
|
appState.getCurrentEnv().currentResponse = result.response
|
||||||
|
|
||||||
|
// Re-render
|
||||||
|
this.hide()
|
||||||
|
answerViewerUI.render(0)
|
||||||
|
} catch (error) {
|
||||||
|
showToast(error.message, 'error')
|
||||||
|
} finally {
|
||||||
|
loadingUI.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Преимущества**:
|
||||||
|
- Понятно где искать функцию
|
||||||
|
- Легко тестировать
|
||||||
|
- Явные зависимости
|
||||||
|
- Переиспользуемый код
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Автор: Claude Sonnet 4.5*
|
||||||
|
*Дата: 2025-12-25*
|
||||||
Loading…
Reference in New Issue