| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- "use client";
- import { useState, useEffect, useRef, useCallback } from "react";
- import { Header } from "@/components/layout";
- import { AppLayout } from "@/components/layout";
- import { Button } from "@/components/ui/button";
- import { Card, CardContent } from "@/components/ui/card";
- import { Loader2, RefreshCw, AlertCircle } from "lucide-react";
- import { EnhancedQueueList } from "@/components/features/queue/enhanced-queue-list";
- import {
- apiClient,
- type QueueStatus,
- type JobInfo,
- } from "@/lib/api";
- import { toast } from "sonner";
- export default function QueuePage() {
- const [queueStatus, setQueueStatus] = useState<QueueStatus | null>(null);
- const [loading, setLoading] = useState(true);
- const [actionLoading, setActionLoading] = useState(false);
- const [error, setError] = useState<string | null>(null);
- const intervalRef = useRef<NodeJS.Timeout | null>(null);
- const isMountedRef = useRef(true);
- // Fetch queue status from the API
- const fetchQueueStatus = useCallback(async () => {
- if (!isMountedRef.current) return;
-
- try {
- const status = await apiClient.getQueueStatus();
- if (isMountedRef.current) {
- setQueueStatus(status);
- setError(null);
- }
- } catch (err) {
- if (isMountedRef.current) {
- const errorMessage = err instanceof Error ? err.message : "Failed to fetch queue status";
- setError(errorMessage);
- console.error("Error fetching queue status:", err);
- }
- } finally {
- if (isMountedRef.current) {
- setLoading(false);
- }
- }
- }, []);
- // Set up intelligent auto-refresh with proper cleanup
- useEffect(() => {
- // Initial fetch
- fetchQueueStatus();
- // Set up interval for auto-refresh
- const startAutoRefresh = () => {
- if (intervalRef.current) {
- clearInterval(intervalRef.current);
- }
-
- intervalRef.current = setInterval(() => {
- fetchQueueStatus();
- }, 2000); // Refresh every 2 seconds for better responsiveness
- };
- startAutoRefresh();
- // Cleanup function
- return () => {
- isMountedRef.current = false;
- if (intervalRef.current) {
- clearInterval(intervalRef.current);
- intervalRef.current = null;
- }
- };
- }, [fetchQueueStatus]);
- // Adjust refresh interval based on queue activity
- useEffect(() => {
- if (!queueStatus?.jobs) return;
- const hasActiveJobs = queueStatus.jobs.some(
- job => job.status === 'processing' || job.status === 'loading' || job.status === 'queued'
- );
- // Use faster refresh for active jobs, slower for idle
- const newInterval = hasActiveJobs ? 1000 : 3000;
-
- if (intervalRef.current) {
- clearInterval(intervalRef.current);
- intervalRef.current = setInterval(() => {
- fetchQueueStatus();
- }, newInterval);
- }
- }, [queueStatus, fetchQueueStatus]);
- // Handle manual refresh
- const handleRefresh = useCallback(() => {
- setLoading(true);
- fetchQueueStatus();
- }, [fetchQueueStatus]);
- // Handle job cancellation
- const handleCancelJob = useCallback(async (jobId: string) => {
- setActionLoading(true);
- try {
- await apiClient.cancelJob(jobId);
- toast.success(`Job ${jobId} cancelled successfully`);
- // Refresh queue status after cancellation
- fetchQueueStatus();
- } catch (err) {
- const errorMessage = err instanceof Error ? err.message : "Failed to cancel job";
- toast.error(`Failed to cancel job: ${errorMessage}`);
- console.error(`Failed to cancel job: ${errorMessage}`, err);
- } finally {
- setActionLoading(false);
- }
- }, [fetchQueueStatus]);
- // Handle queue clearing
- const handleClearQueue = useCallback(async () => {
- if (!queueStatus?.jobs.length) return;
-
- if (!confirm("Are you sure you want to clear all jobs from the queue? This action cannot be undone.")) {
- return;
- }
- setActionLoading(true);
- try {
- await apiClient.clearQueue();
- toast.success("Queue cleared successfully");
- // Refresh queue status after clearing
- fetchQueueStatus();
- } catch (err) {
- const errorMessage = err instanceof Error ? err.message : "Failed to clear queue";
- toast.error(`Failed to clear queue: ${errorMessage}`);
- console.error(`Failed to clear queue: ${errorMessage}`, err);
- } finally {
- setActionLoading(false);
- }
- }, [queueStatus?.jobs.length, fetchQueueStatus]);
- // Handle copying job parameters
- const handleCopyParameters = useCallback((job: JobInfo) => {
- try {
- const params = {
- prompt: job.prompt || "",
- negative_prompt: job.message || "",
- // Add other relevant parameters as needed
- };
-
- const paramsText = JSON.stringify(params, null, 2);
- navigator.clipboard.writeText(paramsText);
- toast.success("Job parameters copied to clipboard");
- } catch (err) {
- toast.error("Failed to copy parameters");
- console.error("Failed to copy parameters:", err);
- }
- }, []);
- return (
- <AppLayout>
- <Header
- title="Queue Management"
- description="Monitor and manage generation jobs in the queue"
- />
- <div className="container mx-auto p-6">
- {error && (
- <Card className="mb-6 border-red-200 bg-red-50 dark:border-red-800 dark:bg-red-950/20">
- <CardContent className="pt-6">
- <div className="flex items-center gap-3 text-red-700 dark:text-red-300">
- <AlertCircle className="h-5 w-5" />
- <div>
- <h3 className="font-medium">Error loading queue</h3>
- <p className="text-sm">{error}</p>
- </div>
- <Button
- variant="outline"
- size="sm"
- onClick={handleRefresh}
- className="ml-auto"
- >
- <RefreshCw className="h-4 w-4 mr-2" />
- Retry
- </Button>
- </div>
- </CardContent>
- </Card>
- )}
- {loading && !queueStatus ? (
- <Card>
- <CardContent className="pt-6">
- <div className="flex items-center justify-center py-12">
- <Loader2 className="h-8 w-8 animate-spin mr-3" />
- <span>Loading queue status...</span>
- </div>
- </CardContent>
- </Card>
- ) : (
- <EnhancedQueueList
- queueStatus={queueStatus}
- loading={loading}
- onRefresh={handleRefresh}
- onCancelJob={handleCancelJob}
- onClearQueue={handleClearQueue}
- actionLoading={actionLoading}
- onCopyParameters={handleCopyParameters}
- />
- )}
- </div>
- </AppLayout>
- );
- }
|