agent_tracker/templates/base.html
nickviljoen 938691e598 Add filtered XLSX export, fix completion reminders, consolidate admin UI
Export & filtering
- Replace /api/admin/agents/export/csv with /api/admin/agents/export/xlsx
  (openpyxl). Multi-line system prompts stay inside one cell instead of
  fragmenting into thousands of rows when opened in Excel.
- Accept filter query params on export: status, discipline, audit,
  business_entity, agent_classification, autonomy_level, risks_only, search.
- Move Export/Import CSV/Delete by CSV buttons into the Agents Management
  tab, drop the duplicate links from the top nav, and rebuild the cramped
  filter row as a wrappable two-row layout.
- Add a Discipline dropdown to the Agents Management filter row to match
  the Prompt Audit tab.

Completion-reminder emails (fix for the broken Complete-button links)
- Add h:Reply-To header to every Mailgun send so users can reply to a real
  mailbox instead of noreply@. Default Nick.Viljoen@oliver.agency, overridable
  via NOTIFICATION_REPLY_TO env var.
- send_completion_reminders now skips with an error log when
  AGENTHUB_PUBLIC_URL is unset instead of mailing relative links email
  clients can't follow.

UI polish
- Restrict the 5-second alert auto-hide to .alert-dismissible so the
  load-bearing 'unresolved owner' banner stays visible until acted on.
- Move the orange brand gradient to a fixed body::before pseudo-element so
  it can't be covered by the white content card or scrolled out of view.
- Lock the navbar to viewport top (position: fixed) and reserve body
  padding-top so content doesn't sit beneath it.

Audit polish (carried over from previous WIP)
- Add batch-state tracking to audit_analyzer for in-flight progress visibility.
- Update PLAN-prompt-audit.md to match the shipped behaviour.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 22:45:29 +02:00

101 lines
No EOL
4.1 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}AgentHub - Modern Agent Management{% endblock %}</title>
<meta name="description" content="Modern Flask user management application with beautiful UI">
<!-- Preconnect for performance -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Google Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" integrity="sha512-9usAa10IRO0HhonpyAIVpjrylPvoDwiPUiKdWk5t3PyolY1cOd4DSE0Ga+ri4AuTroPR5aQvXU9xC6qOPnzFeg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-LN+7fdVzj6u52u30Kp6M/trliBMCMKTyK833zpbD+pXdCLuTusPj697FH4R/5mcr" crossorigin="anonymous">
<!-- Chart.js -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<!-- Custom CSS -->
<link rel="stylesheet" href="{{ base_path }}/static/style.css">
{% block style %}{% endblock %}
<!-- Favicon -->
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>👥</text></svg>">
</head>
<body>
{% include "nav.html" %}
<main class="main-content">
<!-- Flash Messages -->
{% if error %}
<div class="container mt-3">
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="fas fa-exclamation-circle me-2"></i>
{{ error }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
</div>
{% endif %}
{% if success %}
<div class="container mt-3">
<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" aria-label="Close"></button>
</div>
</div>
{% endif %}
{% block content %} {% endblock %}
</main>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/js/bootstrap.bundle.min.js" integrity="sha384-ndDqU0Gzau9qJ1lfW4pNLlhNTkCfHzAVBReH9diLvGRem5+R9g2FzA8ZGN954O5Q" crossorigin="anonymous"></script>
<!-- Custom JS -->
<script>
// Add smooth scrolling
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
document.querySelector(this.getAttribute('href')).scrollIntoView({
behavior: 'smooth'
});
});
});
// Add loading states to forms
document.querySelectorAll('form').forEach(form => {
form.addEventListener('submit', function(e) {
const submitBtn = form.querySelector('button[type="submit"]');
if (submitBtn) {
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Processing...';
submitBtn.disabled = true;
}
});
});
// Auto-hide dismissible flash messages after 5 seconds.
// Permanent banners (e.g. the unresolved-owner notice on the admin dashboard)
// are .alert without .alert-dismissible and must stay visible until acted on.
setTimeout(() => {
document.querySelectorAll('.alert.alert-dismissible').forEach(alert => {
alert.style.transition = 'opacity 0.5s ease';
alert.style.opacity = '0';
setTimeout(() => alert.remove(), 500);
});
}, 5000);
</script>
{% block scripts %}{% endblock %}
</body>
</html>