Update agents, dashboard, kb +2 more (+14 ~20 -3)
This commit is contained in:
@@ -498,7 +498,7 @@
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.activity-icon.done {
|
||||
.activity-icon.done, .activity-icon.task {
|
||||
background: rgba(34, 197, 94, 0.2);
|
||||
color: #22c55e;
|
||||
}
|
||||
@@ -508,11 +508,33 @@
|
||||
color: #3b82f6;
|
||||
}
|
||||
|
||||
.activity-icon.cron {
|
||||
background: rgba(168, 85, 247, 0.2);
|
||||
color: #a855f7;
|
||||
}
|
||||
|
||||
.activity-icon.git {
|
||||
background: rgba(249, 115, 22, 0.2);
|
||||
color: #f97316;
|
||||
}
|
||||
|
||||
.activity-icon.file {
|
||||
background: rgba(20, 184, 166, 0.2);
|
||||
color: #14b8a6;
|
||||
}
|
||||
|
||||
.activity-icon svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.activity-type {
|
||||
font-size: var(--text-xs);
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
background: var(--bg-surface);
|
||||
}
|
||||
|
||||
.activity-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
@@ -732,6 +754,7 @@
|
||||
}
|
||||
|
||||
.issue-owner.marius { color: #22c55e; }
|
||||
.issue-owner.robert { color: #f59e0b; }
|
||||
.issue-owner.clawdbot { color: #8b5cf6; }
|
||||
|
||||
.issue-date {
|
||||
@@ -786,7 +809,7 @@
|
||||
}
|
||||
|
||||
.modal {
|
||||
background: var(--bg-surface);
|
||||
background: var(--bg-base);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--space-5);
|
||||
@@ -794,6 +817,7 @@
|
||||
max-width: 500px;
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
@@ -978,28 +1002,28 @@
|
||||
|
||||
<!-- Stats Summary -->
|
||||
<div class="stats-summary" id="statsSummary">
|
||||
<div class="stat-card">
|
||||
<div class="stat-card" title="Task-uri completate azi">
|
||||
<div class="stat-icon"><i data-lucide="check-circle"></i></div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-value" id="statToday">0</div>
|
||||
<div class="stat-label">Azi</div>
|
||||
<div class="stat-label">Tasks azi</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-card" title="Task-uri completate săptămâna aceasta">
|
||||
<div class="stat-icon"><i data-lucide="calendar"></i></div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-value" id="statWeek">0</div>
|
||||
<div class="stat-label">Săptămâna</div>
|
||||
<div class="stat-label">Tasks săpt.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-card" title="Task-uri completate luna aceasta">
|
||||
<div class="stat-icon"><i data-lucide="trending-up"></i></div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-value" id="statMonth">0</div>
|
||||
<div class="stat-label">Luna</div>
|
||||
<div class="stat-label">Tasks lună</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-card" title="Număr de fișiere insights">
|
||||
<div class="stat-icon"><i data-lucide="lightbulb"></i></div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-value" id="statInsights">0</div>
|
||||
@@ -1055,6 +1079,7 @@
|
||||
<div class="issues-filters" id="issuesFilters">
|
||||
<button class="filter-btn active" data-filter="all">Toate</button>
|
||||
<button class="filter-btn" data-filter="marius">👤 Marius</button>
|
||||
<button class="filter-btn" data-filter="robert">👷 Robert</button>
|
||||
<button class="filter-btn" data-filter="clawdbot">🤖 Clawdbot</button>
|
||||
</div>
|
||||
<div class="panel-body" id="issuesBody">
|
||||
@@ -1090,6 +1115,7 @@
|
||||
<label class="form-label">Owner</label>
|
||||
<select class="input" id="issueOwner">
|
||||
<option value="marius">👤 Marius</option>
|
||||
<option value="robert">👷 Robert</option>
|
||||
<option value="clawdbot">🤖 Clawdbot</option>
|
||||
</select>
|
||||
</div>
|
||||
@@ -1343,42 +1369,53 @@
|
||||
}
|
||||
|
||||
async function loadCronStatus() {
|
||||
const now = new Date();
|
||||
const hour = now.getHours();
|
||||
|
||||
// TODO: În viitor, fetch din /api/cron
|
||||
const jobs = [
|
||||
{ time: '06:30', name: 'morning-report', done: hour >= 7 },
|
||||
{ time: '07:00', name: 'morning-coaching', done: hour >= 7 },
|
||||
{ time: '18:00', name: 'evening-report', done: hour >= 18 },
|
||||
{ time: '19:00', name: 'evening-coaching', done: hour >= 19 }
|
||||
];
|
||||
|
||||
const doneCount = jobs.filter(j => j.done).length;
|
||||
|
||||
// Update badge
|
||||
const badge = document.getElementById('cronBadge');
|
||||
badge.textContent = `${doneCount}/${jobs.length}`;
|
||||
badge.className = 'status-badge ok';
|
||||
|
||||
// Update subtitle
|
||||
const subtitle = document.getElementById('cronSubtitle');
|
||||
const nextJob = jobs.find(j => !j.done);
|
||||
subtitle.textContent = nextJob
|
||||
? `Următorul: ${nextJob.time} ${nextJob.name}`
|
||||
: 'Toate job-urile au rulat azi';
|
||||
|
||||
// Update details
|
||||
const details = document.getElementById('cronDetails');
|
||||
details.innerHTML = jobs.map(job => `
|
||||
<div class="cron-item ${job.done ? 'done' : 'pending'}">
|
||||
<i data-lucide="${job.done ? 'check-circle' : 'clock'}" class="cron-icon ${job.done ? 'done' : 'pending'}"></i>
|
||||
<span class="cron-time">${job.time}</span>
|
||||
<span class="cron-name">${job.name}</span>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
lucide.createIcons();
|
||||
try {
|
||||
const response = await fetch('./api/cron?' + Date.now());
|
||||
if (!response.ok) throw new Error('API error');
|
||||
const data = await response.json();
|
||||
|
||||
const jobs = data.jobs || [];
|
||||
const ranToday = data.ranToday || 0;
|
||||
|
||||
// Update badge
|
||||
const badge = document.getElementById('cronBadge');
|
||||
badge.textContent = `${ranToday}/${jobs.length}`;
|
||||
badge.className = 'status-badge ok';
|
||||
|
||||
// Update subtitle - find next job to run
|
||||
const subtitle = document.getElementById('cronSubtitle');
|
||||
const now = Date.now();
|
||||
const pendingJobs = jobs.filter(j => !j.ranToday);
|
||||
const nextJob = pendingJobs.length > 0 ? pendingJobs[0] : null;
|
||||
subtitle.textContent = nextJob
|
||||
? `Următorul: ${nextJob.time} ${nextJob.name}`
|
||||
: 'Toate job-urile au rulat azi';
|
||||
|
||||
// Update details
|
||||
const details = document.getElementById('cronDetails');
|
||||
details.innerHTML = jobs.map(job => {
|
||||
const done = job.ranToday;
|
||||
const failed = job.lastStatus === 'error';
|
||||
const statusClass = failed ? 'failed' : (done ? 'done' : 'pending');
|
||||
const icon = failed ? 'x-circle' : (done ? 'check-circle' : 'clock');
|
||||
|
||||
return `
|
||||
<div class="cron-item ${statusClass}">
|
||||
<i data-lucide="${icon}" class="cron-icon ${statusClass}"></i>
|
||||
<span class="cron-time">${job.time}</span>
|
||||
<span class="cron-name">${job.name}</span>
|
||||
<span class="cron-agent" style="color: var(--text-muted); font-size: 0.75rem; margin-left: auto;">${job.agentId || ''}</span>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
lucide.createIcons();
|
||||
} catch (e) {
|
||||
console.error('Error loading cron status:', e);
|
||||
const badge = document.getElementById('cronBadge');
|
||||
badge.textContent = 'eroare';
|
||||
badge.className = 'status-badge error';
|
||||
}
|
||||
}
|
||||
|
||||
async function loadAgentsStatus() {
|
||||
@@ -1461,30 +1498,22 @@
|
||||
|
||||
async function loadActivity() {
|
||||
try {
|
||||
// Fetch from tasks.json
|
||||
const response = await fetch('tasks.json?t=' + Date.now());
|
||||
// Fetch from unified activity API
|
||||
const response = await fetch('./api/activity?t=' + Date.now());
|
||||
const data = await response.json();
|
||||
|
||||
// Collect all tasks from all columns with their status
|
||||
let allTasks = [];
|
||||
data.columns.forEach(col => {
|
||||
col.tasks.forEach(task => {
|
||||
const timestamp = task.completed || task.created || '';
|
||||
allTasks.push({
|
||||
type: col.id === 'done' ? 'done' : (col.id === 'in-progress' ? 'running' : 'pending'),
|
||||
text: task.title,
|
||||
agent: task.agent || 'Echo',
|
||||
time: formatActivityTime(timestamp),
|
||||
timestamp: new Date(timestamp).getTime() || 0
|
||||
});
|
||||
});
|
||||
});
|
||||
if (data.error) throw new Error(data.error);
|
||||
|
||||
// Sort descending by timestamp (newest first)
|
||||
allTasks.sort((a, b) => b.timestamp - a.timestamp);
|
||||
|
||||
// Take only recent items (last 20)
|
||||
activityData = allTasks.slice(0, 20);
|
||||
activityData = (data.activities || []).map(a => ({
|
||||
type: a.type,
|
||||
icon: a.icon || 'activity',
|
||||
text: a.text,
|
||||
agent: a.agent || 'Echo',
|
||||
time: a.time,
|
||||
timestamp: a.timestamp,
|
||||
status: a.status,
|
||||
path: a.path
|
||||
}));
|
||||
|
||||
} catch (e) {
|
||||
console.error('Failed to load activity:', e);
|
||||
@@ -1574,28 +1603,32 @@
|
||||
|
||||
const today = new Date().toLocaleDateString('ro-RO', { day: 'numeric', month: 'short' });
|
||||
|
||||
// Group by type for better display
|
||||
const typeLabels = {
|
||||
'cron': '⏰ Cron Jobs',
|
||||
'git': '📦 Git Commits',
|
||||
'file': '📄 Fișiere',
|
||||
'task': '✅ Task-uri'
|
||||
};
|
||||
|
||||
body.innerHTML = `
|
||||
<div class="activity-section">
|
||||
<div class="activity-section-title">
|
||||
<i data-lucide="calendar"></i>
|
||||
Azi (${today})
|
||||
<i data-lucide="activity"></i>
|
||||
Ultimele 24h
|
||||
</div>
|
||||
${activityData.map(item => `
|
||||
<div class="activity-item">
|
||||
<div class="activity-item" ${item.path ? `onclick="window.open('files.html#${item.path}', '_blank')" style="cursor:pointer"` : ''}>
|
||||
<div class="activity-icon ${item.type}">
|
||||
<i data-lucide="${item.type === 'running' ? 'loader' : 'check'}"></i>
|
||||
<i data-lucide="${item.icon || 'activity'}"></i>
|
||||
</div>
|
||||
<div class="activity-content">
|
||||
<div class="activity-text">${item.text}</div>
|
||||
<div class="activity-meta">
|
||||
<span class="activity-type">${typeLabels[item.type] || item.type}</span>
|
||||
<span class="activity-agent">${item.agent}</span>
|
||||
<span>${item.time}</span>
|
||||
</div>
|
||||
${item.progress ? `
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" style="width: ${item.progress}%"></div>
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
</div>
|
||||
`).join('')}
|
||||
@@ -1683,7 +1716,8 @@
|
||||
|
||||
function renderIssueItem(issue) {
|
||||
const isDone = issue.status === 'done';
|
||||
const ownerIcon = issue.owner === 'clawdbot' ? '🤖' : '👤';
|
||||
const ownerIcons = { 'clawdbot': '🤖', 'robert': '👷', 'marius': '👤' };
|
||||
const ownerIcon = ownerIcons[issue.owner] || '👤';
|
||||
const dateStr = new Date(issue.created).toLocaleDateString('ro-RO', { day: 'numeric', month: 'short' });
|
||||
|
||||
return `
|
||||
@@ -1695,7 +1729,7 @@
|
||||
<div class="issue-title">${issue.title}</div>
|
||||
<div class="issue-meta">
|
||||
${issue.program ? `<span class="issue-tag program">${issue.program}</span>` : ''}
|
||||
<span class="issue-owner ${issue.owner}">${ownerIcon} ${issue.owner === 'clawdbot' ? 'Clawdbot' : 'Marius'}</span>
|
||||
<span class="issue-owner ${issue.owner}">${ownerIcon} ${issue.owner === 'clawdbot' ? 'Clawdbot' : (issue.owner === 'robert' ? 'Robert' : 'Marius')}</span>
|
||||
<span class="issue-date">${dateStr}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user