agent_tracker/templates/agent_management.html
2025-08-17 07:23:53 -05:00

1002 lines
No EOL
37 KiB
HTML

{% extends "base.html" %}
{% block title %}Agent Management - AgentHub{% endblock %}
{% block content %}
<div class="container my-5">
<div class="row">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h2><i class="fas {{ 'fa-globe' if current_view == 'all' else 'fa-robot' }} me-3"></i>{{ page_title }}</h2>
<p class="text-muted mb-0">{{ page_description }}</p>
</div>
<div class="d-flex gap-2">
<a href="{{ base_path }}/agent-register" class="btn btn-success">
<i class="fas fa-plus me-2"></i>Register New Agent
</a>
<a href="{{ base_path }}/search" class="btn btn-outline-primary">
<i class="fas fa-search me-2"></i>Search
</a>
</div>
</div>
<!-- Success/Error Messages -->
{% if success %}
<div class="row mb-3">
<div class="col-12">
<div class="alert alert-success alert-dismissible fade show" role="alert">
<i class="fas fa-check-circle me-2"></i>{{ success }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
</div>
</div>
{% endif %}
{% if error %}
<div class="row mb-3">
<div class="col-12">
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="fas fa-exclamation-triangle me-2"></i>{{ error }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
</div>
</div>
{% endif %}
<!-- View Toggle Tabs -->
<div class="row mb-4">
<div class="col-12">
<div class="card shadow-sm border-0">
<div class="card-body py-3">
<div class="d-flex justify-content-center">
<div class="btn-group" role="group" aria-label="Agent view toggle">
<input type="radio" class="btn-check" name="viewToggle" id="allAgentsTab" autocomplete="off" {% if current_view == 'all' %}checked{% endif %}>
<label class="btn btn-outline-primary" for="allAgentsTab">
<i class="fas fa-globe me-2"></i>All Agents
<span class="badge bg-primary ms-2" id="allAgentsCount">{{ agent_count if current_view == 'all' else '0' }}</span>
</label>
<input type="radio" class="btn-check" name="viewToggle" id="myAgentsTab" autocomplete="off" {% if current_view == 'my' %}checked{% endif %}>
<label class="btn btn-outline-primary" for="myAgentsTab">
<i class="fas fa-robot me-2"></i>My Agents
<span class="badge bg-primary ms-2" id="myAgentsCount">{{ agent_count if current_view == 'my' else '0' }}</span>
</label>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Filters and Search -->
<div class="row mb-4">
<div class="col-12">
<div class="card shadow-sm border-0">
<div class="card-body">
<div class="row align-items-center">
<div class="col-md-4 mb-3 mb-md-0">
<div class="input-group">
<span class="input-group-text"><i class="fas fa-search"></i></span>
<input type="text" class="form-control" id="searchInput" placeholder="Search agents...">
</div>
</div>
<div class="col-md-3 mb-3 mb-md-0">
<select class="form-select" id="statusFilter">
<option value="">All Statuses</option>
<option value="Active">Active</option>
<option value="Development">Development</option>
<option value="Inactive">Inactive</option>
<option value="Deprecated">Deprecated</option>
</select>
</div>
<div class="col-md-3 mb-3 mb-md-0">
<select class="form-select" id="sortBy">
<option value="created_at">Sort by Created Date</option>
<option value="name">Sort by Name</option>
<option value="status">Sort by Status</option>
</select>
</div>
<div class="col-md-2">
<button class="btn btn-outline-primary w-100" id="refreshBtn">
<i class="fas fa-sync-alt"></i>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Agents List -->
<div class="row">
<div class="col-12">
<div class="card shadow-sm border-0">
<div class="card-header bg-white">
<div class="d-flex justify-content-between align-items-center">
<h5 class="mb-0" id="agentListTitle">{{ 'All AI Agents' if current_view == 'all' else 'Your AI Agents' }}</h5>
<span class="badge bg-primary" id="agentCount">{{ agent_count }}</span>
</div>
</div>
<div class="card-body p-0">
<div id="agentsContainer">
{% if agents %}
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="bg-light">
<tr>
<th>Agent Name</th>
<th>Status</th>
<th>Version</th>
<th>Department</th>
<th>Created</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for agent in agents %}
<tr>
<td>
<div class="d-flex align-items-center">
<div class="agent-avatar me-3">
<i class="fas fa-robot"></i>
</div>
<div>
<h6 class="mb-0">{{ agent.agent_name }}</h6>
<small class="text-muted">{{ agent.agent_description[:50] }}{% if agent.agent_description|length > 50 %}...{% endif %}</small>
</div>
</div>
</td>
<td>
{% set status_class = {'Active': 'success', 'Development': 'warning', 'Inactive': 'secondary', 'Deprecated': 'danger'} %}
<span class="badge bg-{{ status_class.get(agent.agent_status, 'secondary') }}">
{{ agent.agent_status or 'Unknown' }}
</span>
</td>
<td>{{ agent.agent_version or 'N/A' }}</td>
<td>{{ agent.agent_department or 'N/A' }}</td>
<td>{{ agent.created_at.strftime('%Y-%m-%d') if agent.created_at else 'N/A' }}</td>
<td>
<div class="btn-group" role="group">
<button type="button" class="btn btn-sm btn-outline-info" onclick="viewAgent('{{ agent._id|string }}')">
<i class="fas fa-eye"></i>
</button>
{% if agent.created_by == current_user._id|string %}
<button type="button" class="btn btn-sm btn-outline-warning" onclick="editAgent('{{ agent._id|string }}')">
<i class="fas fa-edit"></i>
</button>
<button type="button" class="btn btn-sm btn-outline-danger" onclick="confirmDelete('{{ agent._id|string }}')">
<i class="fas fa-trash"></i>
</button>
{% else %}
<button type="button" class="btn btn-sm btn-outline-secondary" disabled title="You can only edit your own agents">
<i class="fas fa-edit"></i>
</button>
<button type="button" class="btn btn-sm btn-outline-secondary" disabled title="You can only delete your own agents">
<i class="fas fa-trash"></i>
</button>
{% endif %}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="text-center py-5">
<div class="mb-3">
<i class="fas fa-robot fa-3x text-muted"></i>
</div>
<h5>No Agents Yet</h5>
<p class="text-muted">You haven't created any agents yet. Click the button below to get started!</p>
<a href="{{ base_path }}/agent-register" class="btn btn-success">
<i class="fas fa-plus me-2"></i>Create Your First Agent
</a>
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Agent Details Modal -->
<div class="modal fade" id="agentModal" tabindex="-1">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Agent Details</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div id="modalContent"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<div id="modalActionButtons"></div>
</div>
</div>
</div>
</div>
<!-- Edit Agent Modal -->
<div class="modal fade" id="editModal" tabindex="-1">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Edit Agent</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="editAgentForm">
<input type="hidden" id="editAgentId">
<div class="row">
<div class="col-md-6 mb-3">
<label for="editAgentName" class="form-label">Agent Name *</label>
<input type="text" class="form-control" id="editAgentName" required>
</div>
<div class="col-md-6 mb-3">
<label for="editAgentStatus" class="form-label">Status</label>
<select class="form-select" id="editAgentStatus">
<option value="Development">Development</option>
<option value="Active">Active</option>
<option value="Inactive">Inactive</option>
<option value="Deprecated">Deprecated</option>
</select>
</div>
</div>
<div class="mb-3">
<label for="editAgentDescription" class="form-label">Description</label>
<textarea class="form-control" id="editAgentDescription" rows="3" maxlength="300"></textarea>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="editAgentPurpose" class="form-label">Purpose</label>
<input type="text" class="form-control" id="editAgentPurpose" maxlength="200">
</div>
<div class="col-md-6 mb-3">
<label for="editAgentVersion" class="form-label">Version</label>
<input type="text" class="form-control" id="editAgentVersion">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="editAgentLocation" class="form-label">Location</label>
<input type="text" class="form-control" id="editAgentLocation">
</div>
<div class="col-md-6 mb-3">
<label for="editAgentDepartment" class="form-label">Department</label>
<input type="text" class="form-control" id="editAgentDepartment">
</div>
</div>
<div class="mb-3">
<label for="editAgentContact" class="form-label">Contact Person</label>
<input type="text" class="form-control" id="editAgentContact">
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="editAgentTags" class="form-label">Tags</label>
<input type="text" class="form-control" id="editAgentTags" placeholder="Separate with commas">
</div>
<div class="col-md-6 mb-3">
<label for="editAgentUserbase" class="form-label">Target Userbase</label>
<input type="text" class="form-control" id="editAgentUserbase" placeholder="Separate with commas">
</div>
</div>
<div class="mb-3">
<label for="editAgentCapabilities" class="form-label">Capabilities</label>
<input type="text" class="form-control" id="editAgentCapabilities" placeholder="Separate with commas">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" form="editAgentForm" class="btn btn-primary">Save Changes</button>
</div>
</div>
</div>
</div>
<style>
.agent-card {
border: 1px solid #e9ecef;
border-radius: 12px;
padding: 20px;
margin-bottom: 15px;
transition: all 0.3s ease;
cursor: pointer;
}
.agent-card:hover {
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
transform: translateY(-2px);
}
.agent-status {
font-size: 0.8rem;
padding: 4px 8px;
border-radius: 20px;
}
.status-Active { background-color: #d4edda; color: #155724; }
.status-Development { background-color: #fff3cd; color: #856404; }
.status-Inactive { background-color: #f8d7da; color: #721c24; }
.status-Deprecated { background-color: #e2e3e5; color: #41464b; }
.agent-icon {
width: 50px;
height: 50px;
border-radius: 50%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 1.5rem;
}
.agent-avatar {
width: 35px;
height: 35px;
border-radius: 8px;
background: linear-gradient(135deg, #007bff 0%, #0056b3 100%);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 1rem;
}
.no-agents {
text-align: center;
padding: 60px 20px;
color: #6c757d;
}
.card-header {
border-radius: 15px 15px 0 0 !important;
}
</style>
<script>
let agents = [];
let allAgents = [];
let myAgents = [];
let currentAgentId = null;
let currentView = '{{ current_view }}';
let currentUserId = '{{ current_user._id|string }}';
document.addEventListener('DOMContentLoaded', function() {
// Load both datasets initially for accurate counts
Promise.all([loadAllAgents(), loadMyAgents()]).then(() => {
// Display the current view
loadAgentsForCurrentView();
});
setupEventListeners();
});
function setupEventListeners() {
document.getElementById('searchInput').addEventListener('input', filterAgents);
document.getElementById('statusFilter').addEventListener('change', filterAgents);
document.getElementById('sortBy').addEventListener('change', sortAndDisplayAgents);
document.getElementById('refreshBtn').addEventListener('click', function() {
// Reload both datasets to ensure counts are accurate
Promise.all([loadAllAgents(), loadMyAgents()]).then(() => {
// Display the current view
loadAgentsForCurrentView();
});
});
// Note: editAgentBtn and deleteAgentBtn event listeners are now added dynamically in showAgentDetails
document.getElementById('editAgentForm').addEventListener('submit', updateAgent);
// Tab switching event listeners
document.getElementById('allAgentsTab').addEventListener('change', function() {
if (this.checked) switchToView('all');
});
document.getElementById('myAgentsTab').addEventListener('change', function() {
if (this.checked) switchToView('my');
});
}
async function loadAgentsForCurrentView() {
if (currentView === 'all') {
agents = allAgents;
} else {
agents = myAgents;
}
displayAgents(agents);
updateAgentCounts();
}
async function loadAllAgents() {
try {
const response = await fetch('{{ base_path }}/api/agents/all', {
credentials: 'include'
});
if (response.ok) {
allAgents = await response.json();
agents = allAgents;
displayAgents(agents);
updateAgentCounts();
} else if (response.status === 401) {
window.location.href = '{{ base_path }}/login';
} else {
throw new Error('Failed to load all agents');
}
} catch (error) {
console.error('Error loading all agents:', error);
showError('Failed to load all agents');
}
}
async function loadMyAgents() {
try {
const response = await fetch('{{ base_path }}/api/agents', {
credentials: 'include'
});
if (response.ok) {
myAgents = await response.json();
agents = myAgents;
displayAgents(agents);
updateAgentCounts();
} else if (response.status === 401) {
window.location.href = '{{ base_path }}/login';
} else {
throw new Error('Failed to load my agents');
}
} catch (error) {
console.error('Error loading my agents:', error);
showError('Failed to load my agents');
}
}
function switchToView(view) {
if (view === currentView) return;
currentView = view;
// Update page title and description
const pageTitle = document.querySelector('h2 i');
const pageDescription = document.querySelector('.text-muted');
const agentListTitle = document.getElementById('agentListTitle');
if (view === 'all') {
pageTitle.className = 'fas fa-globe me-3';
pageTitle.nextSibling.textContent = 'All Agents';
agentListTitle.textContent = 'All AI Agents';
agents = allAgents;
if (allAgents.length === 0) {
loadAllAgents();
return;
}
} else {
pageTitle.className = 'fas fa-robot me-3';
pageTitle.nextSibling.textContent = 'My Agents Dashboard';
agentListTitle.textContent = 'Your AI Agents';
agents = myAgents;
if (myAgents.length === 0) {
loadMyAgents();
return;
}
}
displayAgents(agents);
updateAgentCounts();
// Update URL without page reload
const newUrl = view === 'my' ? '/agent-management?view=my' : '/agent-management';
window.history.pushState({view: view}, '', newUrl);
}
function displayAgents(agentsToShow) {
const container = document.getElementById('agentsContainer');
if (agentsToShow.length === 0) {
container.innerHTML = `
<div class="no-agents">
<i class="fas fa-robot fa-3x mb-3"></i>
<h5>No agents found</h5>
<p class="mb-3">You haven't created any AI agents yet.</p>
<a href="{{ base_path }}/agent-register" class="btn btn-success">
<i class="fas fa-plus me-2"></i>Create Your First Agent
</a>
</div>
`;
return;
}
const agentsHtml = agentsToShow.map(agent => `
<div class="agent-card" onclick="showAgentDetails('${agent.agent_id}')">
<div class="row align-items-center">
<div class="col-auto">
<div class="agent-icon">
<i class="fas fa-robot"></i>
</div>
</div>
<div class="col">
<div class="d-flex justify-content-between align-items-start">
<div>
<h6 class="mb-1 fw-bold">${agent.agent_name}</h6>
<p class="text-muted mb-2 small">${agent.agent_description || 'No description'}</p>
<div class="d-flex gap-2 align-items-center">
<span class="agent-status status-${agent.agent_status || 'Development'}">
${agent.agent_status || 'Development'}
</span>
${agent.agent_version ? `<span class="badge bg-light text-dark">v${agent.agent_version}</span>` : ''}
</div>
</div>
<div class="text-end">
<small class="text-muted">${formatDate(agent.agent_created_at)}</small>
<div class="mt-1">
${agent.created_by === currentUserId ? `
<button class="btn btn-outline-primary btn-sm me-1" onclick="event.stopPropagation(); editAgent('${agent.agent_id}')">
<i class="fas fa-edit"></i>
</button>
<button class="btn btn-outline-danger btn-sm" onclick="event.stopPropagation(); confirmDelete('${agent.agent_id}')">
<i class="fas fa-trash"></i>
</button>
` : `
<button class="btn btn-outline-secondary btn-sm" disabled title="You can only edit your own agents">
<i class="fas fa-edit"></i>
</button>
<button class="btn btn-outline-secondary btn-sm" disabled title="You can only delete your own agents">
<i class="fas fa-trash"></i>
</button>
`}
</div>
</div>
</div>
</div>
</div>
</div>
`).join('');
container.innerHTML = agentsHtml;
}
function showAgentDetails(agentId) {
const agent = agents.find(a => a.agent_id === agentId);
if (!agent) return;
currentAgentId = agentId;
const modalContent = `
<div class="row">
<div class="col-md-6">
<h6>Basic Information</h6>
<table class="table table-sm">
<tr><td><strong>Name:</strong></td><td>${agent.agent_name}</td></tr>
<tr><td><strong>Status:</strong></td><td><span class="agent-status status-${agent.agent_status || 'Development'}">${agent.agent_status || 'Development'}</span></td></tr>
<tr><td><strong>Version:</strong></td><td>${agent.agent_version || 'N/A'}</td></tr>
<tr><td><strong>Purpose:</strong></td><td>${agent.agent_purpose || 'N/A'}</td></tr>
</table>
</div>
<div class="col-md-6">
<h6>Details</h6>
<table class="table table-sm">
<tr><td><strong>Location:</strong></td><td>${agent.agent_location || 'N/A'}</td></tr>
<tr><td><strong>Department:</strong></td><td>${agent.agent_department || 'N/A'}</td></tr>
<tr><td><strong>Contact:</strong></td><td>${agent.agent_contact_person || 'N/A'}</td></tr>
<tr><td><strong>Created:</strong></td><td>${formatDate(agent.agent_created_at)}</td></tr>
</table>
</div>
</div>
${agent.agent_description ? `
<div class="row mt-3">
<div class="col-12">
<h6>Description</h6>
<p class="text-muted">${agent.agent_description}</p>
</div>
</div>
` : ''}
${agent.agent_tags && agent.agent_tags.length > 0 ? `
<div class="row mt-3">
<div class="col-12">
<h6>Tags</h6>
<div>
${agent.agent_tags.map(tag => `<span class="badge bg-secondary me-1">${tag}</span>`).join('')}
</div>
</div>
</div>
` : ''}
${agent.agent_capabilities && agent.agent_capabilities.length > 0 ? `
<div class="row mt-3">
<div class="col-12">
<h6>Capabilities</h6>
<div>
${agent.agent_capabilities.map(cap => `<span class="badge bg-info me-1">${cap}</span>`).join('')}
</div>
</div>
</div>
` : ''}
${agent.agent_userbase && agent.agent_userbase.length > 0 ? `
<div class="row mt-3">
<div class="col-12">
<h6>Target Userbase</h6>
<div>
${agent.agent_userbase.map(user => `<span class="badge bg-success me-1">${user}</span>`).join('')}
</div>
</div>
</div>
` : ''}
<div class="row mt-4">
<div class="col-12">
<h6>Usage Analytics</h6>
<div class="card">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center mb-3">
<div class="btn-group" role="group">
<input type="radio" class="btn-check" name="period" id="dailyPeriod" value="daily" checked>
<label class="btn btn-outline-primary btn-sm" for="dailyPeriod">Daily</label>
<input type="radio" class="btn-check" name="period" id="weeklyPeriod" value="weekly">
<label class="btn btn-outline-primary btn-sm" for="weeklyPeriod">Weekly</label>
<input type="radio" class="btn-check" name="period" id="monthlyPeriod" value="monthly">
<label class="btn btn-outline-primary btn-sm" for="monthlyPeriod">Monthly</label>
</div>
<div class="d-flex gap-2">
<input type="date" class="form-control form-control-sm" id="startDate" placeholder="Start date">
<input type="date" class="form-control form-control-sm" id="endDate" placeholder="End date">
<button class="btn btn-primary btn-sm" id="updateChart">Update</button>
</div>
</div>
<div id="usageStatsContainer">
<div class="text-center text-muted">
<div class="spinner-border spinner-border-sm" role="status"></div>
<p class="mt-2">Loading usage data...</p>
</div>
</div>
<canvas id="usageChart" style="display: none; max-height: 300px;"></canvas>
</div>
</div>
</div>
</div>
`;
document.getElementById('modalContent').innerHTML = modalContent;
// Create action buttons based on ownership and admin status
const isOwner = agent.created_by === currentUserId;
const isAdmin = {{ 'true' if current_user.role == 'admin' else 'false' }};
let actionButtonsHtml = '';
if (isOwner || isAdmin) {
actionButtonsHtml = `
<button type="button" class="btn btn-warning" id="editAgentBtn">Edit</button>
<button type="button" class="btn btn-danger" id="deleteAgentBtn">Delete</button>
`;
}
document.getElementById('modalActionButtons').innerHTML = actionButtonsHtml;
// Add event listeners to the dynamically created buttons
if (isOwner || isAdmin) {
document.getElementById('editAgentBtn').addEventListener('click', showEditModal);
document.getElementById('deleteAgentBtn').addEventListener('click', deleteAgent);
}
const modal = new bootstrap.Modal(document.getElementById('agentModal'));
modal.show();
// Load usage data after modal is shown
loadUsageChart(agent.agent_name);
}
function showEditModal() {
const agent = agents.find(a => a.agent_id === currentAgentId);
if (!agent) return;
// Populate edit form
document.getElementById('editAgentId').value = agent.agent_id;
document.getElementById('editAgentName').value = agent.agent_name;
document.getElementById('editAgentStatus').value = agent.agent_status || 'Development';
document.getElementById('editAgentDescription').value = agent.agent_description || '';
document.getElementById('editAgentPurpose').value = agent.agent_purpose || '';
document.getElementById('editAgentVersion').value = agent.agent_version || '';
document.getElementById('editAgentLocation').value = agent.agent_location || '';
document.getElementById('editAgentDepartment').value = agent.agent_department || '';
document.getElementById('editAgentContact').value = agent.agent_contact_person || '';
document.getElementById('editAgentTags').value = agent.agent_tags ? agent.agent_tags.join(', ') : '';
document.getElementById('editAgentUserbase').value = agent.agent_userbase ? agent.agent_userbase.join(', ') : '';
document.getElementById('editAgentCapabilities').value = agent.agent_capabilities ? agent.agent_capabilities.join(', ') : '';
// Hide details modal and show edit modal
bootstrap.Modal.getInstance(document.getElementById('agentModal')).hide();
const editModal = new bootstrap.Modal(document.getElementById('editModal'));
editModal.show();
}
async function updateAgent(e) {
e.preventDefault();
const agentId = document.getElementById('editAgentId').value;
const agentData = {
agent_name: document.getElementById('editAgentName').value,
agent_status: document.getElementById('editAgentStatus').value,
agent_description: document.getElementById('editAgentDescription').value,
agent_purpose: document.getElementById('editAgentPurpose').value,
agent_version: document.getElementById('editAgentVersion').value,
agent_location: document.getElementById('editAgentLocation').value,
agent_department: document.getElementById('editAgentDepartment').value,
agent_contact_person: document.getElementById('editAgentContact').value,
agent_tags: document.getElementById('editAgentTags').value.split(',').map(s => s.trim()).filter(s => s),
agent_userbase: document.getElementById('editAgentUserbase').value.split(',').map(s => s.trim()).filter(s => s),
agent_capabilities: document.getElementById('editAgentCapabilities').value.split(',').map(s => s.trim()).filter(s => s)
};
try {
const response = await fetch(`{{ base_path }}/api/agents/${agentId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include',
body: JSON.stringify(agentData)
});
if (response.ok) {
bootstrap.Modal.getInstance(document.getElementById('editModal')).hide();
await loadAgentsForCurrentView();
showSuccess('Agent updated successfully');
} else {
const error = await response.json();
showError(error.detail || 'Failed to update agent');
}
} catch (error) {
showError('Failed to update agent');
}
}
async function deleteAgent() {
try {
console.log('🗑️ Frontend: Starting deletion for agent:', currentAgentId);
const response = await fetch(`{{ base_path }}/api/agents/${currentAgentId}`, {
method: 'DELETE',
credentials: 'include'
});
console.log('🗑️ Frontend: Delete response status:', response.status, 'OK:', response.ok);
if (response.ok) {
console.log('🗑️ Frontend: Delete successful, closing modal and reloading');
// Close modal if it's open
const modal = bootstrap.Modal.getInstance(document.getElementById('agentModal'));
if (modal) modal.hide();
console.log('🗑️ Frontend: Reloading agents...');
await loadAgentsForCurrentView();
console.log('🗑️ Frontend: Showing success message');
showSuccess('Agent deleted successfully');
} else {
let errorMessage = 'Failed to delete agent';
try {
const error = await response.json();
errorMessage = error.detail || errorMessage;
} catch (e) {
// If response is not JSON, use default message
errorMessage = `Failed to delete agent (${response.status})`;
}
console.log('🗑️ Frontend: Delete failed:', errorMessage);
showError(errorMessage);
}
} catch (error) {
console.log('🗑️ Frontend: Exception during delete:', error);
showError('Failed to delete agent: ' + error.message);
}
}
function filterAgents() {
const searchTerm = document.getElementById('searchInput').value.toLowerCase();
const statusFilter = document.getElementById('statusFilter').value;
let filtered = agents.filter(agent => {
const matchesSearch = agent.agent_name.toLowerCase().includes(searchTerm) ||
(agent.agent_description || '').toLowerCase().includes(searchTerm);
const matchesStatus = !statusFilter || agent.agent_status === statusFilter;
return matchesSearch && matchesStatus;
});
displayAgents(filtered);
}
function sortAndDisplayAgents() {
const sortBy = document.getElementById('sortBy').value;
const sorted = [...agents].sort((a, b) => {
if (sortBy === 'name') {
return a.agent_name.localeCompare(b.agent_name);
} else if (sortBy === 'status') {
return (a.agent_status || 'Development').localeCompare(b.agent_status || 'Development');
} else {
return new Date(b.agent_created_at) - new Date(a.agent_created_at);
}
});
displayAgents(sorted);
}
function updateAgentCounts() {
document.getElementById('agentCount').textContent = agents.length;
document.getElementById('allAgentsCount').textContent = allAgents.length;
document.getElementById('myAgentsCount').textContent = myAgents.length;
}
function formatDate(dateString) {
if (!dateString) return 'N/A';
const date = new Date(dateString);
return date.toLocaleDateString() + ' ' + date.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
}
function showSuccess(message) {
// Simple alert - could be replaced with a toast notification
alert(message);
}
function showError(message) {
// Simple alert - could be replaced with a toast notification
alert('Error: ' + message);
}
function editAgent(agentId) {
currentAgentId = agentId;
showEditModal();
}
function confirmDelete(agentId) {
currentAgentId = agentId;
if (confirm('Are you sure you want to delete this agent? This action cannot be undone.')) {
deleteAgent();
}
}
function logout() {
window.location.href = '{{ base_path }}/logout';
}
let usageChart = null;
async function loadUsageChart(agentName, period = 'daily', startDate = null, endDate = null) {
try {
// Show loading state
document.getElementById('usageStatsContainer').style.display = 'block';
document.getElementById('usageChart').style.display = 'none';
// Build query parameters
const params = new URLSearchParams({ period });
if (startDate) params.append('start_date', startDate);
if (endDate) params.append('end_date', endDate);
// Fetch usage stats and chart data
const [statsResponse, chartResponse] = await Promise.all([
fetch(`{{ base_path }}/api/agents/${encodeURIComponent(agentName)}/usage?${params}`, {
credentials: 'include'
}),
fetch(`{{ base_path }}/api/agents/${encodeURIComponent(agentName)}/usage/chart?${params}`, {
credentials: 'include'
})
]);
if (!statsResponse.ok || !chartResponse.ok) {
throw new Error('Failed to load usage data');
}
const stats = await statsResponse.json();
const chartData = await chartResponse.json();
// Display statistics
const statsHtml = `
<div class="row text-center mb-3">
<div class="col-4">
<div class="border rounded p-2">
<h6 class="mb-1 text-primary">${stats.total_usage_count}</h6>
<small class="text-muted">Total Usage</small>
</div>
</div>
<div class="col-4">
<div class="border rounded p-2">
<h6 class="mb-1 text-success">${stats.first_usage ? formatDate(stats.first_usage) : 'N/A'}</h6>
<small class="text-muted">First Used</small>
</div>
</div>
<div class="col-4">
<div class="border rounded p-2">
<h6 class="mb-1 text-info">${stats.last_usage ? formatDate(stats.last_usage) : 'N/A'}</h6>
<small class="text-muted">Last Used</small>
</div>
</div>
</div>
`;
document.getElementById('usageStatsContainer').innerHTML = statsHtml;
// Display chart if there's data
if (chartData.labels && chartData.labels.length > 0) {
document.getElementById('usageChart').style.display = 'block';
renderUsageChart(chartData);
} else {
document.getElementById('usageChart').style.display = 'none';
document.getElementById('usageStatsContainer').innerHTML +=
'<div class="text-center text-muted mt-3"><p>No usage data available for the selected period</p></div>';
}
// Setup event listeners for chart controls
setupChartEventListeners(agentName);
} catch (error) {
console.error('Error loading usage chart:', error);
document.getElementById('usageStatsContainer').innerHTML =
'<div class="text-center text-danger"><p>Failed to load usage data</p></div>';
}
}
function renderUsageChart(chartData) {
const ctx = document.getElementById('usageChart').getContext('2d');
// Destroy existing chart if it exists
if (usageChart) {
usageChart.destroy();
}
usageChart = new Chart(ctx, {
type: 'line',
data: chartData,
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: true,
position: 'top'
}
},
scales: {
y: {
beginAtZero: true,
ticks: {
stepSize: 1
}
}
}
}
});
}
function setupChartEventListeners(agentName) {
// Period toggle listeners
document.querySelectorAll('input[name="period"]').forEach(radio => {
radio.addEventListener('change', function() {
if (this.checked) {
const startDate = document.getElementById('startDate').value;
const endDate = document.getElementById('endDate').value;
loadUsageChart(agentName, this.value, startDate, endDate);
}
});
});
// Update chart button listener
document.getElementById('updateChart').addEventListener('click', function() {
const period = document.querySelector('input[name="period"]:checked').value;
const startDate = document.getElementById('startDate').value;
const endDate = document.getElementById('endDate').value;
loadUsageChart(agentName, period, startDate, endDate);
});
}
</script>
{% endblock %}