simple-queue-web 0.1.0

Web UI for inspecting and managing simple-queue persistent job queues backed by PostgreSQL
{% extends "base.html" %}

{% block title %}Dashboard - Queue Manager{% endblock %}

{% block content %}
<div class="space-y-6">
    <h1 class="text-2xl font-bold text-white">Dashboard</h1>

    <div id="counters">
        <div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-3 mb-6">
            <a href="/queues/browse?status=pending" class="bg-gray-900 rounded-lg p-4 border border-gray-800 hover:bg-gray-800 transition-colors">
                <div class="text-xs uppercase tracking-wider text-yellow-500 mb-1">Pending</div>
                <div class="text-2xl font-bold text-white">{{ data.total_pending }}</div>
            </a>
            <a href="/queues/browse?status=running" class="bg-gray-900 rounded-lg p-4 border border-gray-800 hover:bg-gray-800 transition-colors">
                <div class="text-xs uppercase tracking-wider text-blue-500 mb-1">Running</div>
                <div class="text-2xl font-bold text-white">{{ data.total_running }}</div>
            </a>
            <a href="/queues/browse?status=completed" class="bg-gray-900 rounded-lg p-4 border border-gray-800 hover:bg-gray-800 transition-colors">
                <div class="text-xs uppercase tracking-wider text-green-500 mb-1">Completed</div>
                <div class="text-2xl font-bold text-white">{{ data.total_completed }}</div>
            </a>
            <a href="/queues/browse?status=failed" class="bg-gray-900 rounded-lg p-4 border border-gray-800 hover:bg-gray-800 transition-colors">
                <div class="text-xs uppercase tracking-wider text-red-500 mb-1">Failed</div>
                <div class="text-2xl font-bold text-white">{{ data.total_failed }}</div>
            </a>
            <a href="/queues/browse?source=dlq" class="bg-gray-900 rounded-lg p-4 border border-red-900/50 hover:bg-gray-800 transition-colors">
                <div class="text-xs uppercase tracking-wider text-red-400 mb-1">DLQ</div>
                <div class="text-2xl font-bold text-white">{{ data.total_dlq }}</div>
            </a>
            <a href="/queues/browse?source=archive" class="bg-gray-900 rounded-lg p-4 border border-gray-700 hover:bg-gray-800 transition-colors">
                <div class="text-xs uppercase tracking-wider text-gray-400 mb-1">Archive</div>
                <div class="text-2xl font-bold text-white">{{ data.total_archive }}</div>
            </a>
        </div>
    </div>

    {% if !data.queue_breakdown.is_empty() %}
    <div class="bg-gray-900 rounded-lg border border-gray-800 overflow-hidden mb-6">
        <div class="px-4 py-3 border-b border-gray-800 text-sm font-medium text-gray-300">Queue Breakdown</div>
        <div class="overflow-x-auto">
            <table class="w-full text-sm">
                <thead>
                    <tr class="text-left text-gray-400 text-xs uppercase tracking-wider">
                        <th class="px-4 py-2">Queue</th>
                        <th class="px-4 py-2 text-right">Pending</th>
                        <th class="px-4 py-2 text-right">Running</th>
                        <th class="px-4 py-2 text-right">Completed</th>
                        <th class="px-4 py-2 text-right">Failed</th>
                        <th class="px-4 py-2 text-right">Total</th>
                    </tr>
                </thead>
                <tbody>
                    {% for row in data.queue_breakdown %}
                    <tr class="border-t border-gray-800 hover:bg-gray-800/50">
                        <td class="px-4 py-2 text-white">
                            <a href="/queues/browse?queue={{ row.queue }}" class="hover:text-blue-300">{{ row.queue }}</a>
                        </td>
                        <td class="px-4 py-2 text-right text-yellow-400">{{ row.pending }}</td>
                        <td class="px-4 py-2 text-right text-blue-400">{{ row.running }}</td>
                        <td class="px-4 py-2 text-right text-green-400">{{ row.completed }}</td>
                        <td class="px-4 py-2 text-right text-red-400">{{ row.failed }}</td>
                        <td class="px-4 py-2 text-right text-gray-300">{{ row.total }}</td>
                    </tr>
                    {% endfor %}
                </tbody>
            </table>
        </div>
    </div>
    {% endif %}

    {% if !data.dlq_counts.is_empty() %}
    <div class="bg-gray-900 rounded-lg border border-red-900/50 overflow-hidden mb-6">
        <div class="px-4 py-3 border-b border-red-900/30 text-sm font-medium text-red-400">Dead Letter Queue Breakdown</div>
        <div class="overflow-x-auto">
            <table class="w-full text-sm">
                <thead>
                    <tr class="text-left text-gray-400 text-xs uppercase tracking-wider">
                        <th class="px-4 py-2">Queue</th>
                        <th class="px-4 py-2">Status</th>
                        <th class="px-4 py-2 text-right">Count</th>
                    </tr>
                </thead>
                <tbody>
                    {% for c in data.dlq_counts %}
                    <tr class="border-t border-gray-800 hover:bg-gray-800/50">
                        <td class="px-4 py-2 text-white">
                            <a href="/queues/browse?queue={{ c.queue }}&source=dlq" class="hover:text-blue-300">{{ c.queue }}</a>
                        </td>
                        <td class="px-4 py-2 text-gray-300">{{ c.status }}</td>
                        <td class="px-4 py-2 text-right text-red-400 font-mono">{{ c.count }}</td>
                    </tr>
                    {% endfor %}
                </tbody>
            </table>
        </div>
    </div>
    {% endif %}

    <div id="table-stats">
        <div class="bg-gray-900 rounded-lg border border-gray-800 overflow-hidden">
            <div class="px-4 py-3 border-b border-gray-800 text-sm font-medium text-gray-300">Table Health (job_queue)</div>
            <div class="grid grid-cols-1 md:grid-cols-3 gap-0">
                <div class="px-4 py-3 border-b md:border-b-0 md:border-r border-gray-800">
                    <div class="text-xs uppercase tracking-wider text-gray-500 mb-1">Dead Tuples</div>
                    <div class="text-xl font-bold {% if data.dead_tuples > 0 %}text-yellow-400{% else %}text-green-400{% endif %}">
                        {{ data.dead_tuples }}
                    </div>
                </div>
                <div class="px-4 py-3 border-b md:border-b-0 md:border-r border-gray-800">
                    <div class="text-xs uppercase tracking-wider text-gray-500 mb-1">Last Vacuum</div>
                    {% if data.last_vacuum != "Never" %}
                    <div class="text-sm font-mono text-gray-300">{{ data.last_vacuum }}</div>
                    {% else %}
                    <div class="text-sm font-mono text-gray-600">Never</div>
                    {% endif %}
                </div>
                <div class="px-4 py-3">
                    <div class="text-xs uppercase tracking-wider text-gray-500 mb-1">Last Autovacuum</div>
                    {% if data.last_autovacuum != "Never" %}
                    <div class="text-sm font-mono text-gray-300">{{ data.last_autovacuum }}</div>
                    {% else %}
                    <div class="text-sm font-mono text-gray-600">Never</div>
                    {% endif %}
                </div>
            </div>
            <div class="border-t border-gray-800 px-4 py-2">
                <div class="flex items-center gap-6 text-xs text-gray-500">
                    <span>Live: <span class="text-gray-300">{{ data.live_tuples }}</span></span>
                    <span>Ins: <span class="text-gray-300">{{ data.total_inserts }}</span></span>
                    <span>Upd: <span class="text-gray-300">{{ data.total_updates }}</span></span>
                    <span>Del: <span class="text-gray-300">{{ data.total_deletes }}</span></span>
                </div>
            </div>
        </div>
    </div>
</div>
{% endblock %}