// Users.tsx - User Command Center - Complete rewrite import { useState, useMemo } from 'react'; import { useQueryState, parseAsString, parseAsInteger, parseAsArrayOf } from 'nuqs'; import { AppLayout } from '@/components/layout/AppLayout'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Badge } from '@/components/ui/badge'; import { TooltipProvider } from '@/components/ui/tooltip'; import { Search, Plus, LayoutGrid, LayoutList, RefreshCw, Download, Filter, } from 'lucide-react'; // Components import { UsersTable } from '@/features/users/components/UsersTable'; import { UserFilters } from '@/features/users/components/UserFilters'; import { UserMetricsBar } from '@/features/users/components/UserMetricsBar'; import { UserInspectorSheet } from '@/features/users/components/UserInspectorSheet'; import { CreateUserDialog } from '@/features/users/components/CreateUserDialog'; import { SuspensionModal } from '@/features/users/components/SuspensionModal'; import { BanModal } from '@/features/users/components/BanModal'; import { DeleteConfirmDialog } from '@/features/users/components/DeleteConfirmDialog'; import { NotificationComposer } from '@/features/users/components/NotificationComposer'; import { BulkActionsDropdown } from '@/features/users/components/BulkActionsDropdown'; // Data & Types import { mockCrmUsers, mockUserMetrics } from '@/features/users/data/mockUserCrmData'; import type { User, SegmentFilters } from '@/lib/types/user'; import { toast } from 'sonner'; import { cn } from '@/lib/utils'; type ViewMode = 'table' | 'grid'; export default function Users() { // URL State with nuqs const [searchQuery, setSearchQuery] = useQueryState('q', parseAsString.withDefault('')); const [minSpent, setMinSpent] = useQueryState('minSpent', parseAsInteger); const [maxSpent, setMaxSpent] = useQueryState('maxSpent', parseAsInteger); // Local State const [viewMode, setViewMode] = useState('table'); const [filtersOpen, setFiltersOpen] = useState(false); const [filters, setFilters] = useState({}); const [selectedUserIds, setSelectedUserIds] = useState([]); // Modal States const [selectedUser, setSelectedUser] = useState(null); const [inspectorOpen, setInspectorOpen] = useState(false); const [createDialogOpen, setCreateDialogOpen] = useState(false); const [suspensionModalOpen, setSuspensionModalOpen] = useState(false); const [banModalOpen, setBanModalOpen] = useState(false); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [notificationComposerOpen, setNotificationComposerOpen] = useState(false); const [notificationTargetUsers, setNotificationTargetUsers] = useState([]); // Sync URL params to filters const mergedFilters: SegmentFilters = useMemo(() => ({ ...filters, search: searchQuery || filters.search, minSpent: minSpent ?? filters.minSpent, maxSpent: maxSpent ?? filters.maxSpent, }), [filters, searchQuery, minSpent, maxSpent]); // Active filters count const activeFiltersCount = useMemo(() => { let count = 0; if (mergedFilters.search) count++; if (mergedFilters.status?.length) count++; if (mergedFilters.role?.length) count++; if (mergedFilters.tier?.length) count++; if (mergedFilters.tags?.length) count++; if (mergedFilters.minSpent) count++; if (mergedFilters.maxSpent) count++; return count; }, [mergedFilters]); // Filter users const filteredUsers = useMemo(() => { return mockCrmUsers.filter((user) => { // Search filter if (mergedFilters.search) { const query = mergedFilters.search.toLowerCase(); if ( !user.name.toLowerCase().includes(query) && !user.email.toLowerCase().includes(query) && !user.phone.includes(query) && !user.id.toLowerCase().includes(query) ) { return false; } } // Status filter if (mergedFilters.status?.length && !mergedFilters.status.includes(user.status)) { return false; } // Role filter if (mergedFilters.role?.length && !mergedFilters.role.includes(user.role)) { return false; } // Tier filter if (mergedFilters.tier?.length && !mergedFilters.tier.includes(user.tier)) { return false; } // Spent range filter if (mergedFilters.minSpent && user.totalSpent < mergedFilters.minSpent) { return false; } if (mergedFilters.maxSpent && user.totalSpent > mergedFilters.maxSpent) { return false; } // Tags filter if (mergedFilters.tags?.length) { const userTagIds = user.tags.map((t) => t.id); if (!mergedFilters.tags.some((tagId) => userTagIds.includes(tagId))) { return false; } } return true; }); }, [mergedFilters]); // Selected users array const selectedUsers = useMemo( () => mockCrmUsers.filter((u) => selectedUserIds.includes(u.id)), [selectedUserIds] ); // Handlers const handleSelectUser = (user: User) => { setSelectedUser(user); setInspectorOpen(true); }; const handleEditUser = (user: User) => { setSelectedUser(user); toast.info(`Edit user: ${user.name}`); // In real app, this would open an edit modal }; const handleSuspendUser = (user: User) => { setSelectedUser(user); setSuspensionModalOpen(true); }; const handleBanUser = (user: User) => { setSelectedUser(user); setBanModalOpen(true); }; const handleDeleteUser = (user: User) => { setSelectedUser(user); setDeleteDialogOpen(true); }; const handleSendNotification = (user: User) => { setNotificationTargetUsers([user]); setNotificationComposerOpen(true); }; const handleBulkNotification = (users: User[]) => { setNotificationTargetUsers(users); setNotificationComposerOpen(true); }; const handleRefresh = () => { toast.success('Data refreshed'); }; const handleExport = () => { toast.success(`Exported ${filteredUsers.length} users to CSV`); }; const handleFiltersChange = (newFilters: SegmentFilters) => { setFilters(newFilters); // Sync to URL if (newFilters.search !== mergedFilters.search) { setSearchQuery(newFilters.search || null); } if (newFilters.minSpent !== mergedFilters.minSpent) { setMinSpent(newFilters.minSpent || null); } if (newFilters.maxSpent !== mergedFilters.maxSpent) { setMaxSpent(newFilters.maxSpent || null); } }; return (
{/* Metrics Bar */} {/* Toolbar */}
{/* Left: Search */}
setSearchQuery(e.target.value || null)} className="pl-9 bg-white/50 border-white/50" />
{/* Right: Actions */}
{/* Bulk Actions (only when selected) */} setSelectedUserIds([])} onSendNotification={handleBulkNotification} onSuspendUsers={(users) => { if (users.length === 1) { handleSuspendUser(users[0]); } else { toast.info(`Suspend ${users.length} users`); } }} onBanUsers={(users) => { if (users.length === 1) { handleBanUser(users[0]); } else { toast.info(`Ban ${users.length} users`); } }} /> {/* View Toggle */}
{/* Refresh */} {/* Export */} {/* Create User */}
{/* Content Area */}
{/* Filter Sidebar (collapsible) */} {filtersOpen && ( setFiltersOpen(false)} /> )} {/* Main Content */}
{/* Results Count */}

Showing {filteredUsers.length} of{' '} {mockCrmUsers.length} users {selectedUserIds.length > 0 && ( ({selectedUserIds.length} selected) )}

{/* Table View */} {viewMode === 'table' && ( )} {/* Grid View */} {viewMode === 'grid' && (
{filteredUsers.map((user) => (
handleSelectUser(user)} className="group relative flex flex-col gap-4 rounded-xl border border-white/40 bg-white/30 p-5 shadow-neu-sm hover:shadow-neu transition-all duration-300 cursor-pointer backdrop-blur-md overflow-hidden" >
{user.name}

{user.name}

{user.email}

{user.status} ₹{(user.totalSpent / 1000).toFixed(0)}K
{user.tags.slice(0, 2).map((tag) => ( {tag.name} ))} {user.tags.length > 2 && ( +{user.tags.length - 2} )}
))}
)}
{/* Modals & Sheets */} setNotificationTargetUsers([])} />
); }