forked from Rockachopa/Timmy-time-dashboard
Major:
- Extract all inline <style> blocks from 22 Jinja2 templates into
static/css/mission-control.css — single cacheable stylesheet
- Add tox lint check that fails on inline <style> in templates
Minor:
1. Connection status indicator in topbar (green/amber/red dot) reflecting
WebSocket + Ollama reachability, with auto-reconnect
2. Jinja2 {% macro panel(title) %} in macros.html — eliminates repeated
.card.mc-panel markup; index.html converted as example
3. SVG favicon (purple T + orange dot)
4. 30-second TTL cache on _check_ollama() to avoid blocking the event loop
on every health poll (asyncio.to_thread was already in place)
5. Toast notification system (McToast.show) for transient status messages —
wired into connection status for Ollama/WebSocket state changes
Enforcement:
- CLAUDE.md updated with conventions 11-14 (no inline CSS, use panel macro,
use toasts, never block the event loop)
- tox lint + pre-push environments now fail on inline <style> blocks
https://claude.ai/code/session_014FQ785MQdyJQ4BAXrRSo9w
Co-authored-by: Claude <noreply@anthropic.com>
186 lines
5.7 KiB
HTML
186 lines
5.7 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Upgrade Queue{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="mc-panel">
|
|
<div class="mc-panel-header">
|
|
<h1 class="page-title">Upgrade Queue</h1>
|
|
<p class="mc-text-secondary">Review and approve self-modification proposals</p>
|
|
</div>
|
|
|
|
<!-- Pending Upgrades -->
|
|
<div class="mc-section">
|
|
<h2 class="mc-section-title">
|
|
Pending Upgrades
|
|
{% if pending_count > 0 %}
|
|
<span class="mc-badge mc-badge-warning">{{ pending_count }}</span>
|
|
{% endif %}
|
|
</h2>
|
|
|
|
{% if pending %}
|
|
<div class="upgrades-list">
|
|
{% for upgrade in pending %}
|
|
<div class="upgrade-card upgrade-pending" data-id="{{ upgrade.id }}">
|
|
<div class="upgrade-header">
|
|
<h3>{{ upgrade.description }}</h3>
|
|
<span class="mc-badge mc-badge-warning">PENDING</span>
|
|
</div>
|
|
|
|
<div class="upgrade-meta">
|
|
<span class="upgrade-branch">Branch: {{ upgrade.branch_name }}</span>
|
|
<span class="upgrade-time">Proposed: {{ upgrade.proposed_at[11:16] }}</span>
|
|
</div>
|
|
|
|
<div class="upgrade-files">
|
|
Files: {{ upgrade.files_changed|join(', ') }}
|
|
</div>
|
|
|
|
<div class="upgrade-test-status">
|
|
{% if upgrade.test_passed %}
|
|
<span class="test-passed">✓ Tests passed</span>
|
|
{% else %}
|
|
<span class="test-failed">✗ Tests failed</span>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="upgrade-actions">
|
|
<button class="mc-btn mc-btn-primary" onclick="approveUpgrade('{{ upgrade.id }}')">
|
|
Approve
|
|
</button>
|
|
<button class="mc-btn" onclick="rejectUpgrade('{{ upgrade.id }}')">
|
|
Reject
|
|
</button>
|
|
<a href="/self-modify/queue/{{ upgrade.id }}/diff" class="mc-btn mc-btn-secondary">
|
|
View Diff
|
|
</a>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<div class="mc-empty-state" style="padding:2rem; text-align:center;">
|
|
<p>No pending upgrades.</p>
|
|
<p class="mc-text-secondary" style="margin-bottom:1rem;">Upgrades are proposed by the self-modification system when the system identifies improvements. You can also trigger them via work orders or the task queue.</p>
|
|
<div style="display:flex; gap:0.75rem; justify-content:center; flex-wrap:wrap;">
|
|
<a href="/work-orders/queue" class="mc-btn mc-btn-secondary" style="text-decoration:none;">View Work Orders</a>
|
|
<a href="/tasks" class="mc-btn mc-btn-secondary" style="text-decoration:none;">View Task Queue</a>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Approved (Waiting to Apply) -->
|
|
{% if approved %}
|
|
<div class="mc-section">
|
|
<h2 class="mc-section-title">Approved (Ready to Apply)</h2>
|
|
<div class="upgrades-list">
|
|
{% for upgrade in approved %}
|
|
<div class="upgrade-card upgrade-approved">
|
|
<div class="upgrade-header">
|
|
<h3>{{ upgrade.description }}</h3>
|
|
<span class="mc-badge mc-badge-success">APPROVED</span>
|
|
</div>
|
|
<div class="upgrade-actions">
|
|
<button class="mc-btn mc-btn-primary" onclick="applyUpgrade('{{ upgrade.id }}')">
|
|
Apply Now
|
|
</button>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- History -->
|
|
<div class="mc-section">
|
|
<h2 class="mc-section-title">History</h2>
|
|
|
|
{% if applied %}
|
|
<h4>Applied</h4>
|
|
<div class="upgrades-list upgrades-history">
|
|
{% for upgrade in applied %}
|
|
<div class="upgrade-card upgrade-applied">
|
|
<span class="upgrade-desc">{{ upgrade.description }}</span>
|
|
<span class="mc-badge mc-badge-success">APPLIED</span>
|
|
<span class="upgrade-time">{{ upgrade.applied_at[11:16] if upgrade.applied_at else '' }}</span>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if rejected %}
|
|
<h4>Rejected</h4>
|
|
<div class="upgrades-list upgrades-history">
|
|
{% for upgrade in rejected %}
|
|
<div class="upgrade-card upgrade-rejected">
|
|
<span class="upgrade-desc">{{ upgrade.description }}</span>
|
|
<span class="mc-badge mc-badge-secondary">REJECTED</span>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if failed %}
|
|
<h4>Failed</h4>
|
|
<div class="upgrades-list upgrades-history">
|
|
{% for upgrade in failed %}
|
|
<div class="upgrade-card upgrade-failed">
|
|
<span class="upgrade-desc">{{ upgrade.description }}</span>
|
|
<span class="mc-badge mc-badge-danger">FAILED</span>
|
|
<span class="upgrade-error" title="{{ upgrade.error_message }}">⚠️</span>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
async function approveUpgrade(id) {
|
|
if (!confirm('Approve this upgrade?')) return;
|
|
|
|
const response = await fetch(`/self-modify/queue/${id}/approve`, {
|
|
method: 'POST',
|
|
});
|
|
|
|
if (response.ok) {
|
|
window.location.reload();
|
|
} else {
|
|
alert('Failed to approve: ' + await response.text());
|
|
}
|
|
}
|
|
|
|
async function rejectUpgrade(id) {
|
|
if (!confirm('Reject this upgrade? The branch will be deleted.')) return;
|
|
|
|
const response = await fetch(`/self-modify/queue/${id}/reject`, {
|
|
method: 'POST',
|
|
});
|
|
|
|
if (response.ok) {
|
|
window.location.reload();
|
|
} else {
|
|
alert('Failed to reject: ' + await response.text());
|
|
}
|
|
}
|
|
|
|
async function applyUpgrade(id) {
|
|
if (!confirm('Apply this upgrade? This will merge to main.')) return;
|
|
|
|
const response = await fetch(`/self-modify/queue/${id}/apply`, {
|
|
method: 'POST',
|
|
});
|
|
|
|
if (response.ok) {
|
|
alert('Upgrade applied successfully!');
|
|
window.location.reload();
|
|
} else {
|
|
const error = await response.text();
|
|
alert('Failed to apply: ' + error);
|
|
}
|
|
}
|
|
</script>
|
|
|
|
{% endblock %}
|