fix: add date filtering to usage/real endpoint and export
This commit is contained in:
218
public/app.js
218
public/app.js
@@ -599,7 +599,14 @@ function initAgentsPage() {
|
||||
async function loadUsage() {
|
||||
// Try real usage first, fallback to tracked usage
|
||||
try {
|
||||
const realRes = await fetch("/api/usage/real");
|
||||
const from = document.getElementById("usage-from")?.value;
|
||||
const to = document.getElementById("usage-to")?.value;
|
||||
let url = "/api/usage/real";
|
||||
const params = [];
|
||||
if (from) params.push(`from=${from}`);
|
||||
if (to) params.push(`to=${to}`);
|
||||
if (params.length) url += `?${params.join("&")}`;
|
||||
const realRes = await fetch(url);
|
||||
if (realRes.ok) {
|
||||
const realData = await realRes.json();
|
||||
usageStats = {
|
||||
@@ -829,7 +836,7 @@ function initUsagePage() {
|
||||
exportJsonBtn.addEventListener('click', () => {
|
||||
const fromValue = fromInput.value;
|
||||
const toValue = toInput.value;
|
||||
let url = '/api/usage/export?format=json';
|
||||
let url = '/api/usage/export/real?format=json';
|
||||
if (fromValue) url += `&from=${fromValue}`;
|
||||
if (toValue) url += `&to=${toValue}`;
|
||||
window.open(url, '_blank');
|
||||
@@ -838,7 +845,7 @@ function initUsagePage() {
|
||||
exportCsvBtn.addEventListener('click', () => {
|
||||
const fromValue = fromInput.value;
|
||||
const toValue = toInput.value;
|
||||
let url = '/api/usage/export?format=csv';
|
||||
let url = '/api/usage/export/real?format=csv';
|
||||
if (fromValue) url += `&from=${fromValue}`;
|
||||
if (toValue) url += `&to=${toValue}`;
|
||||
window.open(url, '_blank');
|
||||
@@ -881,3 +888,208 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
if (CURRENT_PAGE === 'agents') initAgentsPage();
|
||||
if (CURRENT_PAGE === 'usage') initUsagePage();
|
||||
});
|
||||
|
||||
// ============ GITEA DASHBOARD ============
|
||||
let giteaData = {
|
||||
repos: [],
|
||||
reviews: [],
|
||||
activity: []
|
||||
};
|
||||
|
||||
// Tab switching
|
||||
document.querySelectorAll('.gitea-tabs .tab-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
// Update active tab button
|
||||
document.querySelectorAll('.gitea-tabs .tab-btn').forEach(b => b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
|
||||
// Show corresponding content
|
||||
const tabName = btn.dataset.tab;
|
||||
document.querySelectorAll('.tab-content').forEach(content => {
|
||||
content.classList.remove('active');
|
||||
});
|
||||
document.getElementById(`${tabName}-tab`).classList.add('active');
|
||||
|
||||
// Load data for tab
|
||||
if (tabName === 'swarm') loadGiteaSwarm();
|
||||
else if (tabName === 'reviews') loadGiteaReviews();
|
||||
else if (tabName === 'activity') loadGiteaActivity();
|
||||
});
|
||||
});
|
||||
|
||||
async function loadGiteaSwarm() {
|
||||
try {
|
||||
const response = await fetch('/api/gitea/swarm');
|
||||
const repos = await response.json();
|
||||
giteaData.repos = repos;
|
||||
|
||||
// Update stats
|
||||
const totalRepos = repos.length;
|
||||
const totalPRs = repos.reduce((sum, r) => sum + r.open_prs, 0);
|
||||
const totalIssues = repos.reduce((sum, r) => sum + r.open_issues, 0);
|
||||
const totalBranches = repos.reduce((sum, r) => sum + r.branches, 0);
|
||||
|
||||
document.getElementById('total-repos').textContent = totalRepos;
|
||||
document.getElementById('total-prs').textContent = totalPRs;
|
||||
document.getElementById('total-issues').textContent = totalIssues;
|
||||
document.getElementById('total-branches').textContent = totalBranches;
|
||||
|
||||
// Render repo list
|
||||
const repoList = document.getElementById('repo-list');
|
||||
if (repos.length === 0) {
|
||||
repoList.innerHTML = '<p class="empty">No repositories found</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
repoList.innerHTML = repos.map(repo => `
|
||||
<div class="repo-card">
|
||||
<div class="repo-header">
|
||||
<h3><a href="${repo.html_url}" target="_blank">${repo.name}</a></h3>
|
||||
<span class="repo-stats">
|
||||
⭐ ${repo.stars} 🍴 ${repo.forks}
|
||||
</span>
|
||||
</div>
|
||||
<div class="repo-metrics">
|
||||
<span class="metric ${repo.open_prs > 0 ? 'has-items' : ''}">
|
||||
🔀 ${repo.open_prs} PRs
|
||||
</span>
|
||||
<span class="metric ${repo.open_issues > 0 ? 'has-items' : ''}">
|
||||
🐛 ${repo.open_issues} Issues
|
||||
</span>
|
||||
<span class="metric">
|
||||
🌿 ${repo.branches} Branches
|
||||
</span>
|
||||
</div>
|
||||
<div class="repo-footer">
|
||||
<span class="updated">Updated: ${new Date(repo.updated_at).toLocaleDateString()}</span>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error loading Gitea swarm:', error);
|
||||
document.getElementById('repo-list').innerHTML =
|
||||
`<p class="error">Failed to load repositories: ${error.message}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadGiteaReviews() {
|
||||
try {
|
||||
const response = await fetch('/api/gitea/reviews');
|
||||
const reviews = await response.json();
|
||||
giteaData.reviews = reviews;
|
||||
|
||||
const reviewsList = document.getElementById('reviews-list');
|
||||
if (reviews.length === 0) {
|
||||
reviewsList.innerHTML = '<p class="empty">No pending reviews</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
reviewsList.innerHTML = reviews.map(pr => `
|
||||
<div class="pr-card ${pr.draft ? 'draft' : ''} ${!pr.mergeable ? 'conflict' : ''}">
|
||||
<div class="pr-header">
|
||||
<span class="pr-repo">${pr.repo}</span>
|
||||
<span class="pr-number">#${pr.pr_number}</span>
|
||||
</div>
|
||||
<h3 class="pr-title">
|
||||
<a href="${pr.pr_url}" target="_blank">${pr.pr_title}</a>
|
||||
</h3>
|
||||
<div class="pr-meta">
|
||||
<span class="pr-author">by ${pr.author}</span>
|
||||
<span class="pr-date">${new Date(pr.created_at).toLocaleDateString()}</span>
|
||||
</div>
|
||||
<div class="pr-labels">
|
||||
${pr.labels.map(label =>
|
||||
`<span class="label" style="background-color: #${label.color}">${label.name}</span>`
|
||||
).join('')}
|
||||
</div>
|
||||
${pr.draft ? '<span class="badge draft-badge">Draft</span>' : ''}
|
||||
${!pr.mergeable ? '<span class="badge conflict-badge">Merge Conflict</span>' : ''}
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error loading Gitea reviews:', error);
|
||||
document.getElementById('reviews-list').innerHTML =
|
||||
`<p class="error">Failed to load reviews: ${error.message}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadGiteaActivity() {
|
||||
try {
|
||||
const response = await fetch('/api/gitea/activity');
|
||||
const activities = await response.json();
|
||||
giteaData.activity = activities;
|
||||
|
||||
const activityFeed = document.getElementById('activity-feed');
|
||||
if (activities.length === 0) {
|
||||
activityFeed.innerHTML = '<p class="empty">No recent activity</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
activityFeed.innerHTML = activities.map(act => `
|
||||
<div class="activity-item">
|
||||
<div class="activity-icon">${getActivityIcon(act.op_type)}</div>
|
||||
<div class="activity-content">
|
||||
<div class="activity-header">
|
||||
<a href="${act.repo_url}" class="activity-repo">${act.repo}</a>
|
||||
<span class="activity-time">${timeAgo(act.created_at)}</span>
|
||||
</div>
|
||||
<div class="activity-desc">
|
||||
${act.content || act.op_type}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error loading Gitea activity:', error);
|
||||
document.getElementById('activity-feed').innerHTML =
|
||||
`<p class="error">Failed to load activity: ${error.message}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
function getActivityIcon(type) {
|
||||
const icons = {
|
||||
'create': '✨',
|
||||
'push': '📤',
|
||||
'merge': '🔀',
|
||||
'close': '✅',
|
||||
'reopen': '🔄',
|
||||
'comment': '💬',
|
||||
'label': '🏷️',
|
||||
'milestone': '🎯',
|
||||
'assign': '👤',
|
||||
'review': '👀',
|
||||
'release': '🚀'
|
||||
};
|
||||
return icons[type] || '📝';
|
||||
}
|
||||
|
||||
function timeAgo(dateString) {
|
||||
const now = new Date();
|
||||
const date = new Date(dateString);
|
||||
const seconds = Math.floor((now - date) / 1000);
|
||||
|
||||
if (seconds < 60) return 'just now';
|
||||
if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;
|
||||
if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ago`;
|
||||
if (seconds < 604800) return `${Math.floor(seconds / 86400)}d ago`;
|
||||
return date.toLocaleDateString();
|
||||
}
|
||||
|
||||
// Auto-load Gitea data on page load
|
||||
if (document.querySelector('.gitea-dashboard')) {
|
||||
loadGiteaSwarm();
|
||||
|
||||
// Refresh every 30 seconds
|
||||
setInterval(() => {
|
||||
const activeTab = document.querySelector('.gitea-tabs .tab-btn.active');
|
||||
if (activeTab) {
|
||||
const tabName = activeTab.dataset.tab;
|
||||
if (tabName === 'swarm') loadGiteaSwarm();
|
||||
else if (tabName === 'reviews') loadGiteaReviews();
|
||||
else if (tabName === 'activity') loadGiteaActivity();
|
||||
}
|
||||
}, 30000);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user