ge-tool/components/QueueManagementModal.tsx
2025-12-10 13:41:43 +07:00

151 lines
6.5 KiB
TypeScript
Executable File

import React, { useState, useEffect, useRef } from 'react';
import type { GeIdItem } from './QueueStatus';
import DragHandleIcon from './DragHandleIcon';
interface QueueManagementModalProps {
isOpen: boolean;
onClose: () => void;
queueItems: GeIdItem[];
onReorder: (reorderedItems: GeIdItem[]) => void;
onDelete: (key: string) => void;
}
const QueueManagementModal: React.FC<QueueManagementModalProps> = ({
isOpen,
onClose,
queueItems,
onReorder,
onDelete,
}) => {
const [localItems, setLocalItems] = useState<GeIdItem[]>([]);
const dragItem = useRef<number | null>(null);
const dragOverItem = useRef<number | null>(null);
useEffect(() => {
if (isOpen) {
document.body.style.overflow = 'hidden';
setLocalItems(queueItems.filter(item => item.status === 'waiting'));
} else {
document.body.style.overflow = 'auto';
}
return () => {
document.body.style.overflow = 'auto';
};
}, [isOpen, queueItems]);
if (!isOpen) {
return null;
}
const handleDragStart = (e: React.DragEvent<HTMLDivElement>, position: number) => {
dragItem.current = position;
e.dataTransfer.effectAllowed = 'move';
// Add a delay to allow the ghost image to be created before styling
setTimeout(() => {
e.currentTarget.classList.add('dragging');
}, 0)
};
const handleDragEnter = (e: React.DragEvent<HTMLDivElement>, position: number) => {
dragOverItem.current = position;
};
const handleDrop = (e: React.DragEvent<HTMLDivElement>) => {
if (dragItem.current === null || dragOverItem.current === null) return;
const newItems = [...localItems];
const dragItemContent = newItems[dragItem.current];
newItems.splice(dragItem.current, 1);
newItems.splice(dragOverItem.current, 0, dragItemContent);
dragItem.current = null;
dragOverItem.current = null;
setLocalItems(newItems);
onReorder(newItems); // Update parent state
};
const handleDragEnd = (e: React.DragEvent<HTMLDivElement>) => {
e.currentTarget.classList.remove('dragging');
}
return (
<div
className="fixed inset-0 bg-black bg-opacity-70 z-50 flex justify-center items-center p-4 animate-fade-in-fast"
onClick={onClose}
role="dialog"
aria-modal="true"
>
<div
className="bg-slate-800 border border-slate-700 rounded-2xl shadow-2xl w-full max-w-2xl max-h-[90vh] flex flex-col p-6 animate-slide-up"
onClick={e => e.stopPropagation()}
>
<div className="flex justify-between items-center mb-4 pb-4 border-b border-slate-700">
<h2 className="text-xl font-semibold text-white">Quản Hàng đi</h2>
<button onClick={onClose} className="text-slate-400 hover:text-white transition-colors text-2xl leading-none">&times;</button>
</div>
<div className="flex-grow overflow-y-auto pr-2 -mr-2">
{localItems.length > 0 ? (
<div className="space-y-2">
{localItems.map((item, index) => (
<div
key={item.key}
className="flex items-start justify-between p-3 bg-slate-700/50 rounded-lg group transition-shadow"
draggable
onDragStart={(e) => handleDragStart(e, index)}
onDragEnter={(e) => handleDragEnter(e, index)}
onDragEnd={handleDragEnd}
onDragOver={(e) => e.preventDefault()}
onDrop={handleDrop}
>
<div className="flex items-start flex-1 min-w-0">
<div className="cursor-move text-slate-500 group-hover:text-slate-300 mr-4 pt-1 flex-shrink-0">
<DragHandleIcon />
</div>
<div className="flex-1 min-w-0">
<div className="font-mono text-sm text-slate-200 font-semibold truncate" title={`${item.id} (${item.lang})`}>
{item.id} ({item.lang})
</div>
<pre className="text-slate-400 text-xs font-mono mt-1 whitespace-pre-wrap break-words">{item.usernames}</pre>
</div>
</div>
<button
onClick={() => onDelete(item.key)}
className="text-xs font-medium text-rose-500 hover:text-rose-400 transition-colors opacity-0 group-hover:opacity-100 ml-4 flex-shrink-0"
>
Xoá
</button>
</div>
))}
</div>
) : (
<div className="flex justify-center items-center h-full">
<p className="text-slate-500">Không submit nào đang chờ.</p>
</div>
)}
</div>
</div>
<style>{`
.dragging {
opacity: 0.5;
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.2), 0 4px 6px -4px rgb(0 0 0 / 0.2);
}
@keyframes fade-in-fast {
from { opacity: 0; }
to { opacity: 1; }
}
.animate-fade-in-fast {
animation: fade-in-fast 0.2s ease-out forwards;
}
@keyframes slide-up {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.animate-slide-up {
animation: slide-up 0.3s ease-out forwards;
}
`}</style>
</div>
);
};
export default QueueManagementModal;