heroagent/pkg/servers/ui/views/pages/jobs.jet
2025-05-24 09:24:19 +04:00

224 lines
7.3 KiB
Plaintext

{{ extends "../layouts/base" }}
{{ block title() }}Job Management - HeroApp UI{{ end }}
{{ block body() }}
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Job Management</h1>
<div class="btn-toolbar mb-2 mb-md-0">
<div class="btn-group me-2">
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="refreshJobs()">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-refresh-cw"><polyline points="23 4 23 10 17 10"></polyline><polyline points="1 20 1 14 7 14"></polyline><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path></svg>
Refresh
</button>
</div>
</div>
</div>
<!-- Job Stats Cards -->
<div class="row mb-4">
<div class="col-md-3">
<div class="card text-white bg-primary">
<div class="card-body">
<h5 class="card-title">Total Jobs</h5>
<p class="card-text display-6">0</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-white bg-success">
<div class="card-body">
<h5 class="card-title">Completed Jobs</h5>
<p class="card-text display-6">0</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-white bg-warning">
<div class="card-body">
<h5 class="card-title">Active Jobs</h5>
<p class="card-text display-6">0</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-white bg-danger">
<div class="card-body">
<h5 class="card-title">Error Jobs</h5>
<p class="card-text display-6">0</p>
</div>
</div>
</div>
</div>
<!-- Filters -->
<div class="row mb-4">
<div class="col-md-12">
<div class="card">
<div class="card-header">
<h5>Filters</h5>
</div>
<div class="card-body">
<form id="filterForm" action="/jobs" method="get" class="row g-3">
<div class="col-md-3">
<label for="circle" class="form-label">Circle</label>
<select class="form-select" id="circle" name="circle" onchange="this.form.submit()">
<option value="">All Circles</option>
</select>
</div>
<div class="col-md-3">
<label for="topic" class="form-label">Topic</label>
<select class="form-select" id="topic" name="topic" onchange="this.form.submit()">
<option value="">All Topics</option>
</select>
</div>
<div class="col-md-3">
<label for="status" class="form-label">Status</label>
<select class="form-select" id="status" name="status" onchange="this.form.submit()">
<option value="">All Statuses</option>
<option value="new">New</option>
<option value="active">Active</option>
<option value="done">Done</option>
<option value="error">Error</option>
</select>
</div>
<div class="col-md-3 d-flex align-items-end">
<button type="submit" class="btn btn-primary">Apply Filters</button>
<a href="/jobs" class="btn btn-secondary ms-2">Clear Filters</a>
</div>
</form>
</div>
</div>
</div>
</div>
<div class="row">
<!-- Tree View -->
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h5>Job Tree</h5>
</div>
<div class="card-body">
<div id="jobTree" class="job-tree">
<p>No job tree data available</p>
</div>
</div>
</div>
</div>
<!-- Job List -->
<div class="col-md-8">
<div class="card">
<div class="card-header">
<h5>Job List</h5>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>ID</th>
<th>Circle</th>
<th>Topic</th>
<th>Status</th>
<th>Scheduled</th>
<th>Duration</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr>
<td colspan="7" class="text-center">No jobs available</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
{{ end }}
{{ block scripts() }}
<script>
function toggleCircle(element) {
const topicsContainer = element.nextElementSibling;
const chevron = element.querySelector('svg');
if (topicsContainer.style.display === 'none') {
topicsContainer.style.display = 'block';
chevron.classList.add('rotate-90');
} else {
topicsContainer.style.display = 'none';
chevron.classList.remove('rotate-90');
}
}
function toggleTopic(element) {
const jobsContainer = element.nextElementSibling;
const chevron = element.querySelector('svg');
if (jobsContainer.style.display === 'none') {
jobsContainer.style.display = 'block';
chevron.classList.add('rotate-90');
} else {
jobsContainer.style.display = 'none';
chevron.classList.remove('rotate-90');
}
}
function refreshJobs() {
window.location.reload();
}
</script>
<style>
.job-tree {
font-size: 0.9rem;
}
.circle-header, .topic-header {
cursor: pointer;
padding: 5px;
border-radius: 4px;
display: flex;
align-items: center;
}
.circle-header:hover, .topic-header:hover {
background-color: #f8f9fa;
}
.circle-name, .topic-name {
margin-left: 5px;
font-weight: 500;
}
.badge {
margin-left: 8px;
}
.job-node {
padding: 3px 0;
}
.job-link {
text-decoration: none;
display: flex;
align-items: center;
gap: 5px;
}
.rotate-90 {
transform: rotate(90deg);
}
.circle-node, .topic-node {
margin-bottom: 5px;
}
.topics-container, .jobs-container {
padding-top: 5px;
}
</style>
{{ end }}