import { useState } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { toast } from 'sonner' import { usersApi } from '@/api/users' import type { UserDto, PaginatedResponse, PaymentDto } from '@/types/models' import type { AxiosError } from 'axios' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog' import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog' import { Label } from '@/components/ui/label' import { Checkbox } from '@/components/ui/checkbox' import { Badge } from '@/components/ui/badge' import { packsApi } from '@/api/packs' import { Search, Edit, Trash2, ChevronLeft, ChevronRight, CreditCard } from 'lucide-react' export default function UsersPage() { const queryClient = useQueryClient() const [page, setPage] = useState(1) const [search, setSearch] = useState('') const [selectedUser, setSelectedUser] = useState(null) const [isEditDialogOpen, setIsEditDialogOpen] = useState(false) const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false) const [isPurchasesDialogOpen, setIsPurchasesDialogOpen] = useState(false) const [userToDelete, setUserToDelete] = useState(null) const [userPurchases, setUserPurchases] = useState([]) // Form state const [formData, setFormData] = useState({ name: '', email: '', admin: false, subscription: false, packs: [] as string[], subscriptionFeatures: [] as string[], }) const limit = 20 // Fetch users const { data, isLoading, error } = useQuery>({ queryKey: ['users', page], queryFn: () => usersApi.getUsers({ page, limit }), }) // Fetch packs for selection const { data: packsData } = useQuery({ queryKey: ['packs', 1, 100, ''], queryFn: () => packsApi.getPacks({ page: 1, limit: 100, search: '', showDisabled: true }), }) // Mutations const updateMutation = useMutation({ mutationFn: (user: UserDto) => usersApi.upsertUser(user), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['users'] }) toast.success('User updated successfully') closeEditDialog() }, onError: (error: unknown) => { const axiosError = error as AxiosError<{ message?: string }> toast.error(axiosError.response?.data?.message || 'Failed to update user') }, }) const deleteMutation = useMutation({ mutationFn: (userId: string) => usersApi.deleteUser(userId), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['users'] }) toast.success('User deleted successfully') setIsDeleteDialogOpen(false) setUserToDelete(null) }, onError: (error: unknown) => { const axiosError = error as AxiosError<{ message?: string }> toast.error(axiosError.response?.data?.message || 'Failed to delete user') }, }) const openEditDialog = (user: UserDto) => { setSelectedUser(user) setFormData({ name: user.name || '', email: user.email || '', admin: user.admin, subscription: user.subscription || false, packs: [...user.packs], subscriptionFeatures: [...user.subscriptionFeatures], }) setIsEditDialogOpen(true) } const closeEditDialog = () => { setIsEditDialogOpen(false) setSelectedUser(null) } const openPurchasesDialog = async (user: UserDto) => { if (!user.id) return try { const purchases = await usersApi.getUserPurchases(user.id.toString()) setUserPurchases(purchases) setSelectedUser(user) setIsPurchasesDialogOpen(true) } catch { toast.error('Failed to load user purchases') } } const closePurchasesDialog = () => { setIsPurchasesDialogOpen(false) setSelectedUser(null) setUserPurchases([]) } const handleDelete = (user: UserDto) => { setUserToDelete(user) setIsDeleteDialogOpen(true) } const confirmDelete = () => { if (userToDelete?.id) { deleteMutation.mutate(userToDelete.id.toString()) } } const handleSubmit = (e: React.FormEvent) => { e.preventDefault() if (!selectedUser?.id) { toast.error('User ID is required') return } const userData: UserDto = { id: selectedUser.id, name: formData.name.trim() || undefined, email: formData.email.trim() || undefined, admin: formData.admin, subscription: formData.subscription, packs: formData.packs, purchases: selectedUser.purchases, subscriptionFeatures: formData.subscriptionFeatures, } updateMutation.mutate(userData) } if (error) { return (

Users Management

Error loading users

Failed to load users. Please try again later.

) } return (

Users Management

View and manage user accounts

{/* Search */}
setSearch(e.target.value)} className="max-w-sm" />
{/* Users Table */} All Users ({data?.total || 0}) Manage user accounts and permissions {isLoading ? (
Loading users...
) : ( <> ID Name Email Admin Packs Actions {data?.items .filter(user => search === '' || user.name?.toLowerCase().includes(search.toLowerCase()) || user.id?.toString().includes(search) ) .map((user) => ( {user.id} {user.name || 'No name'} {user.email || 'No email'} {user.admin ? ( Admin ) : ( User )} {user.packs.length} packs
))}
{/* Pagination */} {data && data.totalPages > 1 && (
Showing {((page - 1) * limit) + 1} to {Math.min(page * limit, data.total)} of {data.total} users
Page {page} of {data.totalPages}
)} )}
{/* Edit User Dialog */} Edit User Update user information and permissions
setFormData(prev => ({ ...prev, name: e.target.value }))} placeholder="User name" />
setFormData(prev => ({ ...prev, email: e.target.value }))} placeholder="user@example.com" />
setFormData(prev => ({ ...prev, admin: checked as boolean }))} />
setFormData(prev => ({ ...prev, subscription: checked as boolean }))} />
{!packsData?.items || packsData.items.length === 0 ? (
No packs available
) : ( packsData.items.map((pack) => (
{ if (checked) { setFormData(prev => ({ ...prev, packs: [...prev.packs, pack.id], })) } else { setFormData(prev => ({ ...prev, packs: prev.packs.filter(id => id !== pack.id), })) } }} />
)) )}

Selected: {formData.packs.length} pack(s)

{['premium', 'unlimited', 'ad_free', 'early_access', 'priority_support'].map((feature) => (
{ if (checked) { setFormData(prev => ({ ...prev, subscriptionFeatures: [...prev.subscriptionFeatures, feature], })) } else { setFormData(prev => ({ ...prev, subscriptionFeatures: prev.subscriptionFeatures.filter(f => f !== feature), })) } }} />
))}

Selected: {formData.subscriptionFeatures.length} feature(s)

{/* User Purchases Dialog */} Purchase History Payment history for user: {selectedUser?.name || selectedUser?.id}
{userPurchases.length === 0 ? (
No purchases found for this user
) : ( ID Amount Currency Status Date {userPurchases.map((payment) => ( {payment.id} {payment.amount} {payment.currency} {payment.status} {new Date(payment.createdAt).toLocaleDateString()} ))}
)}
{/* Delete Confirmation Dialog */} Delete User Are you sure you want to delete the user "{userToDelete?.name || userToDelete?.id}"? This action cannot be undone. Cancel {deleteMutation.isPending ? 'Deleting...' : 'Delete'}
) }