206 lines
7.5 KiB
TypeScript
206 lines
7.5 KiB
TypeScript
import { useState, useEffect } from 'react'
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
|
import { Button } from "@/components/ui/button"
|
|
import { Progress } from "@/components/ui/progress"
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
|
import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts'
|
|
|
|
interface Task {
|
|
id: string;
|
|
status: "Processed" | "Processing" | "Queued" | "Exception";
|
|
createdAt: Date;
|
|
title: string;
|
|
description: string;
|
|
expectedProgress: number;
|
|
currentJobDescription: string;
|
|
}
|
|
|
|
const generateMockTasks = (): Task[] => {
|
|
const statuses: Task['status'][] = ["Processed", "Processing", "Queued", "Exception"];
|
|
return Array.from({ length: 50 }, (_, i) => ({
|
|
id: `task-${i + 1}`,
|
|
status: statuses[Math.floor(Math.random() * statuses.length)],
|
|
createdAt: new Date(Date.now() - Math.random() * 10000000000),
|
|
title: `Task ${i + 1}`,
|
|
description: `This is a description for Task ${i + 1}`,
|
|
expectedProgress: Math.random(),
|
|
currentJobDescription: `Current job for Task ${i + 1}`
|
|
}));
|
|
};
|
|
|
|
export default function DynamicWorkQueue() {
|
|
const [tasks, setTasks] = useState<Task[]>([])
|
|
|
|
useEffect(() => {
|
|
setTasks(generateMockTasks());
|
|
|
|
const intervalId = setInterval(() => {
|
|
setTasks(prevTasks => {
|
|
let newTasks: Task[] = prevTasks;
|
|
// if processed tasks are more than 10, remove the oldest one
|
|
if (newTasks.filter(task => task.status === "Processed").length > 10) {
|
|
newTasks = newTasks.filter(task => task.status !== "Processed");
|
|
}
|
|
// update the progress of each task
|
|
newTasks = newTasks.map(task => ({
|
|
...task,
|
|
expectedProgress: Math.min(1, task.expectedProgress + Math.random() * 0.2),
|
|
status: task.expectedProgress >= 1 ? "Processed" : task.status
|
|
}));
|
|
// if there are no queued tasks, add a new one
|
|
if (newTasks.filter(task => task.status === "Queued").length === 0) {
|
|
newTasks.push({
|
|
id: `task-${newTasks.length + 1}`,
|
|
status: "Queued",
|
|
createdAt: new Date(),
|
|
title: `Task ${newTasks.length + 1}`,
|
|
description: `This is a description for Task ${newTasks.length + 1}`,
|
|
expectedProgress: 0,
|
|
currentJobDescription: `Current job for Task ${newTasks.length + 1}`
|
|
});
|
|
}
|
|
return newTasks;
|
|
});
|
|
}, 5000);
|
|
|
|
return () => clearInterval(intervalId);
|
|
}, [])
|
|
|
|
const updateTaskStatus = (taskId: string, newStatus: Task['status']) => {
|
|
setTasks(prevTasks =>
|
|
prevTasks.map(task =>
|
|
task.id === taskId ? { ...task, status: newStatus } : task
|
|
)
|
|
);
|
|
}
|
|
|
|
const renderTaskList = (status: Task['status']) => {
|
|
const filteredTasks = tasks.filter(task => task.status === status)
|
|
|
|
return (
|
|
<>
|
|
{filteredTasks.length === 0 ? (
|
|
<p className="text-gray-500 p-4">No tasks</p>
|
|
) : (
|
|
<ul className="space-y-4 p-4">
|
|
{filteredTasks.map(task => (
|
|
<li key={task.id} className="border p-4 rounded-md shadow-sm">
|
|
<h4 className="font-medium text-primary">{task.title}</h4>
|
|
<p className="text-sm text-gray-600">{task.description}</p>
|
|
<p className="text-sm text-gray-500 mt-1">Created: {task.createdAt.toLocaleString()}</p>
|
|
<div className="mt-2">
|
|
<Progress value={task.expectedProgress * 100} className="h-2" />
|
|
<p className="text-xs text-right mt-1">{Math.round(task.expectedProgress * 100)}%</p>
|
|
</div>
|
|
<p className="text-sm mt-1 text-gray-700">{task.currentJobDescription}</p>
|
|
{status === "Queued" && (
|
|
<Button
|
|
onClick={() => updateTaskStatus(task.id, "Processing")}
|
|
className="mt-2"
|
|
size="sm"
|
|
>
|
|
Start Processing
|
|
</Button>
|
|
)}
|
|
{status === "Processing" && (
|
|
<Button
|
|
onClick={() => updateTaskStatus(task.id, "Processed")}
|
|
className="mt-2"
|
|
size="sm"
|
|
>
|
|
Mark as Processed
|
|
</Button>
|
|
)}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
)}
|
|
</>
|
|
)
|
|
}
|
|
|
|
const renderProcessedTaskSummary = () => {
|
|
const processedTasks = tasks.filter(task => task.status === "Processed");
|
|
const totalProcessed = processedTasks.length;
|
|
const averageProgress = processedTasks.reduce((sum, task) => sum + task.expectedProgress, 0) / totalProcessed || 0;
|
|
|
|
const chartData = [
|
|
{ name: 'Processed', value: totalProcessed },
|
|
{ name: 'Remaining', value: tasks.length - totalProcessed },
|
|
];
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Total Processed</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<p className="text-4xl font-bold">{totalProcessed}</p>
|
|
</CardContent>
|
|
</Card>
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Average Progress</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<p className="text-4xl font-bold">{(averageProgress * 100).toFixed(2)}%</p>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Processed vs Remaining Tasks</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<ResponsiveContainer width="100%" height={300}>
|
|
<BarChart data={chartData}>
|
|
<XAxis dataKey="name" />
|
|
<YAxis />
|
|
<Tooltip />
|
|
<Bar dataKey="value" fill="#8884d8" />
|
|
</BarChart>
|
|
</ResponsiveContainer>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="container mx-auto p-4 max-w-4xl">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Dynamic Work Queue</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<Tabs defaultValue="Queued" className="w-full">
|
|
<TabsList className="grid w-full grid-cols-5">
|
|
<TabsTrigger value="Queued">Queued ({tasks.filter(t => t.status === "Queued").length})</TabsTrigger>
|
|
<TabsTrigger value="Processing">Processing ({tasks.filter(t => t.status === "Processing").length})</TabsTrigger>
|
|
<TabsTrigger value="Processed">Processed ({tasks.filter(t => t.status === "Processed").length})</TabsTrigger>
|
|
<TabsTrigger value="Exception">Exception ({tasks.filter(t => t.status === "Exception").length})</TabsTrigger>
|
|
<TabsTrigger value="Summary">Summary</TabsTrigger>
|
|
</TabsList>
|
|
<TabsContent value="Queued">
|
|
{renderTaskList("Queued")}
|
|
</TabsContent>
|
|
<TabsContent value="Processing">
|
|
{renderTaskList("Processing")}
|
|
</TabsContent>
|
|
<TabsContent value="Processed">
|
|
{renderTaskList("Processed")}
|
|
</TabsContent>
|
|
<TabsContent value="Exception">
|
|
{renderTaskList("Exception")}
|
|
</TabsContent>
|
|
<TabsContent value="Summary">
|
|
{renderProcessedTaskSummary()}
|
|
</TabsContent>
|
|
</Tabs>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
)
|
|
}
|