duelfi_admin/app/users/page.tsx
2025-06-08 22:04:00 +05:30

205 lines
7.7 KiB
TypeScript

'use client';
import { useState, useEffect, useMemo } from 'react';
import { User, fetchUsers, truncateAddress } from '../utils/api';
import Pagination from '../components/Pagination';
import SearchInput from '../components/SearchInput';
import Modal from '../components/Modal';
const ITEMS_PER_PAGE = 20;
export default function Users() {
const [users, setUsers] = useState<User[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [currentPage, setCurrentPage] = useState(1);
const [search, setSearch] = useState('');
const [selectedUser, setSelectedUser] = useState<User | null>(null);
const [showUserModal, setShowUserModal] = useState(false);
const handlePlayerClick = (e: React.MouseEvent, playerId: string) => {
e.preventDefault();
e.stopPropagation();
const user = users.find(u => u.active_wallet === playerId);
if (user) {
setSelectedUser(user);
setShowUserModal(true);
}
};
// Filter and paginate users
const filteredUsers = useMemo(() => {
if (!search) return users;
const searchLower = search.toLowerCase();
return users.filter(user =>
user.ref_id.toLowerCase().includes(searchLower) ||
(user.username || '').toLowerCase().includes(searchLower) ||
(user.active_wallet || '').toLowerCase().includes(searchLower) ||
(user.x_profile_url || '').toLowerCase().includes(searchLower) ||
(user.referred_id || '').toLowerCase().includes(searchLower)
);
}, [users, search]);
const currentUsers = filteredUsers.slice(
(currentPage - 1) * ITEMS_PER_PAGE,
currentPage * ITEMS_PER_PAGE
);
useEffect(() => {
const loadData = async () => {
try {
const usersData = await fetchUsers();
setUsers(usersData);
} catch (err) {
setError('Failed to load data. Please try again later.');
console.error('Error loading data:', err);
} finally {
setLoading(false);
}
};
loadData();
}, []);
useEffect(() => {
setCurrentPage(1);
}, [search]);
if (loading) {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="text-lg text-[var(--text-primary)]">Loading...</div>
</div>
);
}
if (error) {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="text-[var(--accent)]">{error}</div>
</div>
);
}
return (
<div className="space-y-6">
{/* User Details Modal */}
<Modal
isOpen={showUserModal}
onClose={() => {
setShowUserModal(false);
setSelectedUser(null);
}}
title="User Details"
>
{selectedUser && (
<div className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-sm font-medium text-[var(--text-secondary)]">Ref ID</p>
<p className="mt-1 text-[var(--text-primary)]">{selectedUser.ref_id}</p>
</div>
<div>
<p className="text-sm font-medium text-[var(--text-secondary)]">Username</p>
<p className="mt-1 text-[var(--text-primary)]">{selectedUser.username || '-'}</p>
</div>
<div>
<p className="text-sm font-medium text-[var(--text-secondary)]">Wallet Address</p>
<p className="mt-1 text-[var(--text-primary)]">{selectedUser.active_wallet || '-'}</p>
</div>
<div>
<p className="text-sm font-medium text-[var(--text-secondary)]">X Profile</p>
<p className="mt-1">
{selectedUser.x_profile_url ? (
<a
href={selectedUser.x_profile_url}
target="_blank"
rel="noopener noreferrer"
className="text-[var(--accent)] hover:text-[var(--accent-hover)]"
onClick={(e) => e.stopPropagation()}
>
View Profile
</a>
) : '-'}
</p>
</div>
<div>
<p className="text-sm font-medium text-[var(--text-secondary)]">Referred By</p>
<p className="mt-1 text-[var(--text-primary)]">{selectedUser.referred_id === '-1' ? '-' : selectedUser.referred_id}</p>
</div>
</div>
</div>
)}
</Modal>
{/* Users Section */}
<div className="bg-[var(--card-bg)] border border-[var(--card-border)] rounded-lg p-6">
<div className="flex justify-between items-center mb-4">
<h2 className="text-xl font-semibold text-[var(--text-primary)]">Users</h2>
<div className="flex items-center space-x-4">
<SearchInput
placeholder="Search users..."
value={search}
onChange={setSearch}
/>
<div className="text-sm text-[var(--text-secondary)]">
Total Users: {filteredUsers.length}
</div>
</div>
</div>
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-[var(--card-border)]">
<thead className="bg-[var(--card-bg)]">
<tr>
<th className="px-4 py-2 text-left text-[var(--text-secondary)]">Ref ID</th>
<th className="px-4 py-2 text-left text-[var(--text-secondary)]">Username</th>
<th className="px-4 py-2 text-left text-[var(--text-secondary)]">Bio</th>
<th className="px-4 py-2 text-left text-[var(--text-secondary)]">X Profile</th>
<th className="px-4 py-2 text-left text-[var(--text-secondary)]">Referred By</th>
<th className="px-4 py-2 text-left text-[var(--text-secondary)]">Active Wallet</th>
</tr>
</thead>
<tbody>
{currentUsers.map((user) => (
<tr
key={user.ref_id}
className="border-t border-[var(--card-border)] hover:bg-[var(--card-bg)] cursor-pointer"
onClick={(e) => handlePlayerClick(e, user.active_wallet)}
>
<td className="px-4 py-2 text-[var(--text-primary)]">{user.ref_id}</td>
<td className="px-4 py-2 text-[var(--text-primary)]">{user.username || '-'}</td>
<td className="px-4 py-2 text-[var(--text-primary)]">{user.bio || '-'}</td>
<td className="px-4 py-2 text-[var(--text-primary)]">
{user.x_profile_url ? (
<a
href={user.x_profile_url}
target="_blank"
rel="noopener noreferrer"
className="text-[var(--accent)] hover:text-[var(--accent-hover)]"
onClick={(e) => e.stopPropagation()}
>
{user.x_profile_url}
</a>
) : '-'}
</td>
<td className="px-4 py-2 text-[var(--text-primary)]">
{user.referred_id === "-1" ? '-' : user.referred_id}
</td>
<td className="px-4 py-2 text-[var(--text-primary)]">
{user.active_wallet ? truncateAddress(user.active_wallet) : '-'}
</td>
</tr>
))}
</tbody>
</table>
<Pagination
totalItems={filteredUsers.length}
itemsPerPage={ITEMS_PER_PAGE}
currentPage={currentPage}
onPageChange={setCurrentPage}
/>
</div>
</div>
</div>
);
}