Files
ZakirovT 057e1f526b init
2026-01-22 00:45:41 +03:00

302 lines
12 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const API_BASE_URL = window.location.origin;
document.addEventListener('DOMContentLoaded', () => {
// Инициализация табов
const tabButtons = document.querySelectorAll('.tab-button');
console.log('Найдено табов:', tabButtons.length);
tabButtons.forEach(button => {
button.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
const tabName = button.getAttribute('data-tab');
console.log('Клик на таб:', tabName);
switchTab(tabName);
});
});
// Traditional summary form
const form = document.getElementById('summaryForm');
const periodSelect = document.getElementById('period');
const customDatesDiv = document.getElementById('customDates');
const loadingDiv = document.getElementById('loading');
const errorDiv = document.getElementById('error');
const resultsDiv = document.getElementById('results');
// AI summary form
const aiForm = document.getElementById('aiSummaryForm');
const aiPeriodSelect = document.getElementById('aiPeriod');
const aiCustomDatesDiv = document.getElementById('aiCustomDates');
// Показываем/скрываем поля для произвольного периода (Traditional)
periodSelect.addEventListener('change', (e) => {
if (e.target.value === 'custom') {
customDatesDiv.style.display = 'grid';
} else {
customDatesDiv.style.display = 'none';
}
});
// Показываем/скрываем поля для произвольного периода (AI)
aiPeriodSelect.addEventListener('change', (e) => {
if (e.target.value === 'custom') {
aiCustomDatesDiv.style.display = 'grid';
} else {
aiCustomDatesDiv.style.display = 'none';
}
});
// Traditional summary submit
form.addEventListener('submit', async (e) => {
e.preventDefault();
errorDiv.style.display = 'none';
resultsDiv.style.display = 'none';
loadingDiv.style.display = 'block';
try {
const repositoryPath = document.getElementById('repositoryPath').value || '.';
const period = document.getElementById('period').value;
const startDate = document.getElementById('startDate').value;
const endDate = document.getElementById('endDate').value;
const requestBody = {
repositoryPath: repositoryPath || undefined
};
if (period === 'custom') {
if (startDate) requestBody.startDate = startDate;
if (endDate) requestBody.endDate = endDate;
} else if (period) {
requestBody.period = period;
}
const response = await fetch(`${API_BASE_URL}/api/summary`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(requestBody)
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || `Ошибка: ${response.statusText}`);
}
const data = await response.json();
displayResults(data, false);
} catch (error) {
showError(error.message);
} finally {
loadingDiv.style.display = 'none';
}
});
// AI summary submit
aiForm.addEventListener('submit', async (e) => {
e.preventDefault();
errorDiv.style.display = 'none';
resultsDiv.style.display = 'none';
loadingDiv.style.display = 'block';
try {
const repositoryPath = document.getElementById('aiRepositoryPath').value || '.';
const period = document.getElementById('aiPeriod').value;
const startDate = document.getElementById('aiStartDate').value;
const endDate = document.getElementById('aiEndDate').value;
const requestBody = {
repositoryPath: repositoryPath || undefined
};
if (period === 'custom') {
if (startDate) requestBody.startDate = startDate;
if (endDate) requestBody.endDate = endDate;
} else if (period) {
requestBody.period = period;
}
const response = await fetch(`${API_BASE_URL}/api/summary/ai`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(requestBody)
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || `Ошибка: ${response.statusText}`);
}
const data = await response.json();
displayResults(data, true);
} catch (error) {
showError(error.message);
} finally {
loadingDiv.style.display = 'none';
}
});
});
function switchTab(tabName) {
console.log('switchTab вызвана с:', tabName);
// Скрыть все tab-content
const tabContents = document.querySelectorAll('.tab-content');
console.log('Найдено контентов:', tabContents.length);
tabContents.forEach(content => {
content.classList.remove('active');
console.log('Скрыт:', content.getAttribute('data-tab'));
});
// Убрать active класс со всех кнопок
const tabButtons = document.querySelectorAll('.tab-button');
console.log('Найдено кнопок:', tabButtons.length);
tabButtons.forEach(button => button.classList.remove('active'));
// Показать выбранный tab
const selectedContent = document.querySelector(`.tab-content[data-tab="${tabName}"]`);
console.log('Выбранный контент:', selectedContent);
if (selectedContent) {
selectedContent.classList.add('active');
console.log('Активирован контент для:', tabName);
}
// Выделить выбранную кнопку
const selectedButton = document.querySelector(`.tab-button[data-tab="${tabName}"]`);
console.log('Выбранная кнопка:', selectedButton);
if (selectedButton) {
selectedButton.classList.add('active');
console.log('Активирована кнопка для:', tabName);
}
}
function showError(message) {
const errorDiv = document.getElementById('error');
errorDiv.textContent = `Ошибка: ${message}`;
errorDiv.style.display = 'block';
}
function displayResults(data, isAiSummary) {
const resultsDiv = document.getElementById('results');
const aiSummaryResult = document.getElementById('aiSummaryResult');
resultsDiv.style.display = 'block';
// Если это AI саммари, показываем его
if (isAiSummary) {
aiSummaryResult.style.display = 'block';
document.getElementById('aiSummaryText').innerHTML = escapeHtml(data.summary || '');
document.getElementById('aiCommitsCount').textContent = data.commitsCount || 0;
} else {
aiSummaryResult.style.display = 'none';
}
// Период
const periodInfo = document.getElementById('periodInfo');
periodInfo.textContent = data.periodInfo?.description || data.period?.description || 'Период не указан';
// Статистика (для traditional summary)
if (!isAiSummary && data.stats) {
const stats = data.stats;
document.getElementById('totalCommits').textContent = formatNumber(stats.totalCommits || 0);
document.getElementById('totalFiles').textContent = formatNumber(stats.totalFilesChanged || 0);
document.getElementById('totalAdditions').textContent = formatNumber(stats.totalAdditions || 0);
document.getElementById('totalDeletions').textContent = formatNumber(stats.totalDeletions || 0);
document.getElementById('uniqueContributors').textContent = formatNumber(stats.uniqueContributors || 0);
// Категории
const categoriesDiv = document.getElementById('categories');
categoriesDiv.innerHTML = '';
if (data.categories && data.categories.length > 0) {
data.categories.forEach(category => {
const categoryCard = document.createElement('div');
categoryCard.className = 'category-card';
categoryCard.innerHTML = `
<div class="category-header">
<div class="category-name">${escapeHtml(category.category)}</div>
<div class="category-count">${category.commitsCount} коммитов</div>
</div>
<div class="category-description">${escapeHtml(category.description)}</div>
${category.keyChanges && category.keyChanges.length > 0 ? `
<div class="key-changes">
<div class="key-changes-title">Ключевые изменения:</div>
${category.keyChanges.map(change => `
<div class="key-change-item">${escapeHtml(change)}</div>
`).join('')}
</div>
` : ''}
`;
categoriesDiv.appendChild(categoryCard);
});
} else {
categoriesDiv.innerHTML = '<p>Категории не найдены</p>';
}
// Топ коммиты
const topCommitsDiv = document.getElementById('topCommits');
topCommitsDiv.innerHTML = '';
if (data.topCommits && data.topCommits.length > 0) {
data.topCommits.forEach(commit => {
const commitCard = document.createElement('div');
commitCard.className = 'commit-item';
const commitDate = new Date(commit.when).toLocaleDateString('ru-RU', {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
commitCard.innerHTML = `
<div class="commit-header">
<div class="commit-message">${escapeHtml(commit.message)}</div>
<div class="commit-sha">${commit.sha.substring(0, 7)}</div>
</div>
<div class="commit-meta">
<span class="commit-author">👤 ${escapeHtml(commit.committer || commit.author)}</span>
${commit.author !== commit.committer
? `<span class="commit-author-info">(Автор: ${escapeHtml(commit.author)})</span>`
: ''
}
<span class="commit-date">📅 ${commitDate}</span>
</div>
<div class="commit-stats">
<span class="stat-badge files">📄 ${commit.filesChanged} файлов</span>
<span class="stat-badge additions"> ${commit.additions} добавлено</span>
<span class="stat-badge deletions"> ${commit.deletions} удалено</span>
</div>
`;
topCommitsDiv.appendChild(commitCard);
});
} else {
topCommitsDiv.innerHTML = '<p>Коммиты не найдены</p>';
}
} else if (isAiSummary) {
// Для AI саммари скрываем статистику
document.querySelector('.stats-grid').style.display = 'none';
document.querySelector('.categories-section').style.display = 'none';
document.querySelector('.top-commits-section').style.display = 'none';
resultsDiv.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
function formatNumber(num) {
return new Intl.NumberFormat('ru-RU').format(num);
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
}