ge-tool/components/UserManagementModal.tsx

175 lines
7.2 KiB
TypeScript
Raw Normal View History

2025-12-10 06:41:43 +00:00
import React, { useState, useEffect } from 'react';
import { sortByProperty } from '../utils/sort-utils';
// Default prefix for TMS username search
const TMS_USERNAME_PREFIX = 'DKI_';
interface TmsUser {
email: string;
name: string;
}
interface UserManagementModalProps {
isOpen: boolean;
onClose: () => void;
onAddUser: (username: string) => void;
}
const UserManagementModal: React.FC<UserManagementModalProps> = ({ isOpen, onClose, onAddUser }) => {
const [searchTerm, setSearchTerm] = useState('');
const [allUsers, setAllUsers] = useState<TmsUser[]>([]);
const [filteredUsers, setFilteredUsers] = useState<TmsUser[]>([]);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
// Load all users when modal opens
useEffect(() => {
if (isOpen) {
document.body.style.overflow = 'hidden';
setSearchTerm('');
setError(null);
loadAllUsers();
} else {
document.body.style.overflow = 'auto';
}
return () => {
document.body.style.overflow = 'auto';
};
}, [isOpen]);
// Filter users when search term changes
useEffect(() => {
if (!searchTerm.trim()) {
setFilteredUsers(sortByProperty(allUsers, u => u.name));
} else {
const term = searchTerm.toLowerCase();
setFilteredUsers(
sortByProperty(
allUsers.filter(u => u.name.toLowerCase().includes(term) || u.email.toLowerCase().includes(term)),
u => u.name
)
);
}
}, [searchTerm, allUsers]);
const loadAllUsers = async () => {
setIsLoading(true);
setError(null);
try {
// Search with DKI_ prefix to get DKI users only
const response = await fetch('/api/user/search', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: TMS_USERNAME_PREFIX }),
});
const data = await response.json();
if (data.success && Array.isArray(data.data)) {
const sortedUsers = sortByProperty(data.data as TmsUser[], u => u.name);
setAllUsers(sortedUsers);
setFilteredUsers(sortedUsers);
} else {
setError('Không thể tải danh sách người dùng');
}
} catch (err) {
console.error('Error loading users:', err);
setError('Lỗi khi tải danh sách người dùng');
} finally {
setIsLoading(false);
}
};
if (!isOpen) {
return null;
}
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 h-[600px] 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">Tìm kiếm người dùng TMS</h2>
<button onClick={onClose} className="text-slate-400 hover:text-white transition-colors text-2xl">&times;</button>
</div>
<div className="mb-4">
<input
type="text"
placeholder="Lọc theo tên hoặc email..."
value={searchTerm}
onChange={e => setSearchTerm(e.target.value)}
className="bg-slate-900/50 border border-slate-700 text-slate-100 text-sm rounded-lg focus:ring-indigo-500 focus:border-indigo-500 block w-full p-2.5"
autoFocus
/>
</div>
{error && (
<div className="mb-4 p-3 bg-rose-900/20 border border-rose-700 rounded-lg text-sm text-rose-300">
{error}
</div>
)}
<div className="flex-grow overflow-y-auto pr-2 -mr-2">
{isLoading ? (
<div className="flex justify-center items-center h-32 text-slate-400">
<span>Đang tải danh sách...</span>
</div>
) : filteredUsers.length === 0 ? (
<div className="flex justify-center items-center h-32 text-slate-400">
<span>{searchTerm ? 'Không tìm thấy người dùng phù hợp' : 'Không có người dùng nào'}</span>
</div>
) : (
<>
<div className="grid grid-cols-2 gap-2 text-sm font-semibold text-slate-400 px-3 py-2 border-b border-slate-700">
<span>Tên</span>
<span>Email</span>
</div>
<div className="divide-y divide-slate-700/50">
{filteredUsers.map((user) => (
<div
key={user.email}
className="grid grid-cols-2 gap-2 items-center p-3"
>
<span className="text-slate-200 font-medium">{user.name}</span>
<span className="text-slate-400 text-sm truncate">{user.email}</span>
</div>
))}
</div>
</>
)}
</div>
{/* Footer - fixed at bottom */}
<div className="mt-4 pt-4 border-t border-slate-700 text-center text-sm text-slate-500">
{searchTerm ? `Tìm thấy ${filteredUsers.length} / ${allUsers.length} người dùng` : `Tổng cộng ${allUsers.length} người dùng`}
</div>
</div>
<style>{`
@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 UserManagementModal;