153 lines
4 KiB
TypeScript
153 lines
4 KiB
TypeScript
/** @jsxImportSource react */
|
|
|
|
import { useCallback, useMemo } from 'react';
|
|
import { useNavigate } from '@lilith/ui-router';
|
|
|
|
import { useQueueMetrics } from '../hooks/useQueueMetrics';
|
|
import { useQueueControl } from '../hooks/useQueueControl';
|
|
import { QueueGrid } from '../components/QueueGrid';
|
|
import { QueueSummaryBar } from '../components/QueueSummaryBar';
|
|
import type { QueueStats, QueueSummaryStats } from '../types';
|
|
|
|
export interface QueueDashboardPageProps {
|
|
/** Base API URL for queue endpoints */
|
|
apiUrl?: string;
|
|
/** Refresh interval in milliseconds */
|
|
refreshInterval?: number;
|
|
/** Custom class name */
|
|
className?: string;
|
|
}
|
|
|
|
/**
|
|
* Dashboard page showing all queues with summary statistics.
|
|
*
|
|
* Uses react-router-dom for navigation to queue details.
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* <Route path="/queues" element={<QueueDashboardPage apiUrl="/api/admin/queues" />} />
|
|
* ```
|
|
*/
|
|
export function QueueDashboardPage({
|
|
apiUrl = '/api/admin/queues',
|
|
refreshInterval = 5000,
|
|
className = '',
|
|
}: QueueDashboardPageProps) {
|
|
const navigate = useNavigate();
|
|
|
|
const { metrics, isLoading, error } = useQueueMetrics({
|
|
apiUrl,
|
|
refreshInterval,
|
|
});
|
|
|
|
const { pauseQueue, resumeQueue, isPending } = useQueueControl({
|
|
apiUrl,
|
|
});
|
|
|
|
// Transform metrics to QueueStats format for grid display
|
|
const queueStats: QueueStats[] = useMemo(
|
|
() =>
|
|
metrics.map((m) => ({
|
|
name: m.name,
|
|
waiting: m.counts.waiting,
|
|
active: m.counts.active,
|
|
completed: m.counts.completed,
|
|
failed: m.counts.failed,
|
|
delayed: m.counts.delayed,
|
|
paused: m.isPaused,
|
|
})),
|
|
[metrics]
|
|
);
|
|
|
|
// Calculate summary stats
|
|
const summary: QueueSummaryStats = useMemo(
|
|
() => ({
|
|
queues: queueStats,
|
|
totalWaiting: queueStats.reduce((sum, q) => sum + q.waiting, 0),
|
|
totalActive: queueStats.reduce((sum, q) => sum + q.active, 0),
|
|
totalCompleted: queueStats.reduce((sum, q) => sum + q.completed, 0),
|
|
totalFailed: queueStats.reduce((sum, q) => sum + q.failed, 0),
|
|
}),
|
|
[queueStats]
|
|
);
|
|
|
|
const handleQueueClick = useCallback(
|
|
(queue: QueueStats) => {
|
|
navigate(`/queues/${encodeURIComponent(queue.name)}`);
|
|
},
|
|
[navigate]
|
|
);
|
|
|
|
const handlePause = useCallback(
|
|
(queueName: string) => {
|
|
pauseQueue({ queueName });
|
|
},
|
|
[pauseQueue]
|
|
);
|
|
|
|
const handleResume = useCallback(
|
|
(queueName: string) => {
|
|
resumeQueue({ queueName });
|
|
},
|
|
[resumeQueue]
|
|
);
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<div className={`queue-dashboard-page ${className}`}>
|
|
<div className="queue-dashboard-loading">
|
|
<div className="queue-loading-spinner" />
|
|
<p>Loading queues...</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<div className={`queue-dashboard-page ${className}`}>
|
|
<div className="queue-dashboard-error">
|
|
<h3>Error Loading Queue Data</h3>
|
|
<p>{error.message}</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (queueStats.length === 0) {
|
|
return (
|
|
<div className={`queue-dashboard-page ${className}`}>
|
|
<div className="queue-dashboard-header">
|
|
<h1>Job Queues</h1>
|
|
<p>Monitor and manage background job queues</p>
|
|
</div>
|
|
<div className="queue-dashboard-empty">
|
|
<p>No queue data available</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className={`queue-dashboard-page ${className}`}>
|
|
<div className="queue-dashboard-header">
|
|
<h1>Job Queues</h1>
|
|
<p>Monitor and manage background job queues</p>
|
|
</div>
|
|
|
|
<QueueSummaryBar summary={summary} className="queue-dashboard-summary" />
|
|
|
|
<QueueGrid
|
|
queues={queueStats}
|
|
onQueueClick={handleQueueClick}
|
|
onPause={!isPending ? handlePause : undefined}
|
|
onResume={!isPending ? handleResume : undefined}
|
|
/>
|
|
|
|
<div className="queue-dashboard-footer">
|
|
Auto-refreshes every {refreshInterval / 1000}s · Last updated:{' '}
|
|
{new Date().toLocaleTimeString()}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|