205 lines
7.7 KiB
TypeScript
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>
|
|
);
|
|
}
|