97 lines
3.7 KiB
TypeScript
97 lines
3.7 KiB
TypeScript
|
|
import React, { memo } from 'react';
|
||
|
|
|
||
|
|
export type GeIdItem = {
|
||
|
|
key: string;
|
||
|
|
id: string;
|
||
|
|
lang: string;
|
||
|
|
status: 'waiting' | 'processing' | 'done' | 'error';
|
||
|
|
usernames?: string;
|
||
|
|
};
|
||
|
|
|
||
|
|
interface QueueStatusProps {
|
||
|
|
currentSubmission: { username: string; geIdAndLang: string } | null;
|
||
|
|
queueItems: GeIdItem[];
|
||
|
|
pendingSubmissionsCount?: number;
|
||
|
|
onOpenQueueModal: () => void;
|
||
|
|
}
|
||
|
|
|
||
|
|
const getStatusStyles = (status: GeIdItem['status']) => {
|
||
|
|
switch (status) {
|
||
|
|
case 'processing':
|
||
|
|
return 'bg-amber-500/20 text-amber-300 border-amber-500/30';
|
||
|
|
case 'done':
|
||
|
|
return 'bg-green-500/20 text-green-300 border-green-500/30';
|
||
|
|
case 'error':
|
||
|
|
return 'bg-rose-500/20 text-rose-300 border-rose-500/30';
|
||
|
|
case 'waiting':
|
||
|
|
default:
|
||
|
|
return 'bg-slate-700/50 text-slate-400 border-slate-600';
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const QueueStatus: React.FC<QueueStatusProps> = ({ currentSubmission, queueItems, pendingSubmissionsCount = 0, onOpenQueueModal }) => {
|
||
|
|
// derive usernames from queueItems when available; fallback to currentSubmission
|
||
|
|
const derivedUsernames = (() => {
|
||
|
|
if (queueItems && queueItems.length > 0) {
|
||
|
|
const all = queueItems.flatMap(i => (i.usernames || '').split('\n').map(s => s.trim()).filter(Boolean));
|
||
|
|
return Array.from(new Set(all));
|
||
|
|
}
|
||
|
|
if (currentSubmission) return currentSubmission.username.split('\n').map(s => s.trim()).filter(Boolean);
|
||
|
|
return [];
|
||
|
|
})();
|
||
|
|
const userCount = derivedUsernames.length;
|
||
|
|
const geIdCount = queueItems.length;
|
||
|
|
|
||
|
|
// Show details only when there are queue items available (currently processing submission)
|
||
|
|
const showDetails = geIdCount > 0;
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="mt-8 bg-slate-800/50 backdrop-blur-sm p-6 rounded-2xl shadow-lg border border-slate-700 min-h-[10rem]">
|
||
|
|
<div className="flex justify-between items-center mb-4 border-b border-slate-700 pb-3">
|
||
|
|
<h2 className="text-xl font-semibold text-white">Hàng đợi và Trạng thái</h2>
|
||
|
|
<span className="text-sm font-semibold tabular-nums rounded-md px-2 py-1 text-slate-400">
|
||
|
|
Submit đang chờ: {pendingSubmissionsCount}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{showDetails ? (
|
||
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 font-mono text-sm animate-fade-in">
|
||
|
|
<div>
|
||
|
|
<h3 className="text-slate-400 mb-2 font-sans font-semibold">Username ({userCount})</h3>
|
||
|
|
<pre className="bg-slate-900/50 p-3 rounded-lg whitespace-pre-wrap break-all h-48 overflow-y-auto">{derivedUsernames.join('\n')}</pre>
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<h3 className="text-slate-400 mb-2 font-sans font-semibold">GE ID & Lang ({geIdCount})</h3>
|
||
|
|
<div className="bg-slate-900/50 p-3 rounded-lg h-48 overflow-y-auto flex flex-wrap gap-2 content-start">
|
||
|
|
{queueItems.map(item => (
|
||
|
|
<button
|
||
|
|
key={item.key}
|
||
|
|
className={`px-2 py-1 text-xs rounded-md border transition-all duration-300 ${getStatusStyles(item.status)}`}
|
||
|
|
disabled
|
||
|
|
>
|
||
|
|
{item.id} {item.lang.toUpperCase()}
|
||
|
|
</button>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
) : (
|
||
|
|
<div className="flex justify-center items-center h-full min-h-[12rem]">
|
||
|
|
<p className="text-slate-500">Không có submit nào đang được xử lý.</p>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
|
||
|
|
<style>{`
|
||
|
|
@keyframes fade-in {
|
||
|
|
from { opacity: 0; transform: translateY(10px); }
|
||
|
|
to { opacity: 1; transform: translateY(0); }
|
||
|
|
}
|
||
|
|
.animate-fade-in {
|
||
|
|
animation: fade-in 0.5s ease-out forwards;
|
||
|
|
}
|
||
|
|
`}</style>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
export default memo(QueueStatus);
|