import React, { useState, useMemo } from 'react'; import { useQuery } from '@tanstack/react-query'; import { Search, User as UserIcon, Check, X, Shield, Briefcase, Coins, ShieldAlert, Users, ChevronDown, ChevronUp, } from 'lucide-react'; import { getUsers } from '../../services/api'; import { User, ActivityStatus } from '../../types'; import { useAuth } from '../../auth/AuthProvider'; export interface SelectedUser { id: string; email: string; name?: string; role?: string; } interface UserSearchPickerProps { selectedUsers: SelectedUser[]; onSelectUser: (user: SelectedUser) => void; onRemoveUser: (userId: string) => void; multiple?: boolean; placeholder?: string; } interface UserGroup { id: string; label: string; icon: React.ElementType; color: string; users: User[]; } export const UserSearchPicker: React.FC = ({ selectedUsers, onSelectUser, onRemoveUser, placeholder = 'Search by name, email, or role...', }) => { const { user, claims } = useAuth(); const currentUserId = String(user?.id || claims?.sub || '').trim().toLowerCase(); const currentUserEmail = String(user?.email || claims?.email || '').trim().toLowerCase(); const [searchTerm, setSearchTerm] = useState(''); const [isDropdownOpen, setIsDropdownOpen] = useState(false); const [collapsedGroups, setCollapsedGroups] = useState>({}); // Query tenant users const { data: users = [], isLoading } = useQuery({ queryKey: ['users-directory'], queryFn: () => getUsers(1, 200), }); // Filter active users and exclude the current logged-in user so they can never message themselves const activeUsers = useMemo(() => { return users.filter(u => { if (u.status !== ActivityStatus.Active || !u.email) return false; const uid = String(u.id || '').trim().toLowerCase(); const uemail = u.email.trim().toLowerCase(); if (currentUserId && uid === currentUserId) return false; if (currentUserEmail && uemail === currentUserEmail) return false; return true; }); }, [users, currentUserId, currentUserEmail]); // Organize users into intuitive functional groups const groupedUsers = useMemo(() => { const term = searchTerm.toLowerCase().trim(); const filtered = activeUsers.filter(u => { if (!term) return true; const email = (u.email || '').toLowerCase(); const role = String(u.role ?? '').toLowerCase(); return email.includes(term) || role.includes(term); }); const adminUsers: User[] = []; const pmUsers: User[] = []; const financeUsers: User[] = []; const meUsers: User[] = []; const otherUsers: User[] = []; filtered.forEach(u => { const r = String(u.role ?? '').toLowerCase(); if (r.includes('admin') || r.includes('executive') || r.includes('director')) { adminUsers.push(u); } else if (r.includes('programme') || r.includes('project') || r.includes('manager')) { pmUsers.push(u); } else if (r.includes('finance') || r.includes('disbursement') || r.includes('accountant') || r.includes('budget')) { financeUsers.push(u); } else if (r.includes('m&e') || r.includes('monitoring') || r.includes('eval') || r.includes('risk') || r.includes('compliance')) { meUsers.push(u); } else { otherUsers.push(u); } }); const groups: UserGroup[] = []; if (adminUsers.length > 0) { groups.push({ id: 'admin', label: 'Leadership & Administration', icon: Shield, color: 'text-amber-400 bg-amber-500/10 border-amber-500/20', users: adminUsers, }); } if (pmUsers.length > 0) { groups.push({ id: 'pm', label: 'Programme & Project Management', icon: Briefcase, color: 'text-blue-400 bg-blue-500/10 border-blue-500/20', users: pmUsers, }); } if (financeUsers.length > 0) { groups.push({ id: 'finance', label: 'Finance & Disbursements', icon: Coins, color: 'text-emerald-400 bg-emerald-500/10 border-emerald-500/20', users: financeUsers, }); } if (meUsers.length > 0) { groups.push({ id: 'me', label: 'M&E, Compliance & Risks', icon: ShieldAlert, color: 'text-purple-400 bg-purple-500/10 border-purple-500/20', users: meUsers, }); } if (otherUsers.length > 0) { groups.push({ id: 'team', label: 'Tenant Staff & Team Members', icon: Users, color: 'text-slate-400 bg-slate-500/10 border-slate-500/20', users: otherUsers, }); } return groups; }, [activeUsers, searchTerm]); const toggleGroupCollapse = (groupId: string) => { setCollapsedGroups(prev => ({ ...prev, [groupId]: !prev[groupId], })); }; const isUserSelected = (userId: string) => { return selectedUsers.some(u => u.id === userId); }; const handleUserClick = (u: User) => { if (!u.email) return; if (isUserSelected(u.id)) { onRemoveUser(u.id); } else { const displayName = u.email.split('@')[0].replace(/[._-]/g, ' '); const formattedName = displayName .split(' ') .map(w => w.charAt(0).toUpperCase() + w.slice(1)) .join(' '); // Strictly single-recipient selection onSelectUser({ id: u.id, email: u.email, name: formattedName, role: String(u.role ?? 'User'), }); setSearchTerm(''); setIsDropdownOpen(false); } }; const singleSelectedUser = selectedUsers[0]; return (
{/* Single Selected Recipient or Search Input Box */} {singleSelectedUser ? (
{(singleSelectedUser.name || singleSelectedUser.email).slice(0, 2).toUpperCase()}
{singleSelectedUser.name || singleSelectedUser.email} <{singleSelectedUser.email}> {singleSelectedUser.role && ( {singleSelectedUser.role} )}
) : (
{ setSearchTerm(e.target.value); if (!isDropdownOpen) setIsDropdownOpen(true); }} onFocus={() => setIsDropdownOpen(true)} placeholder={placeholder} className="w-full pl-9 pr-8 py-2 rounded-xl bg-slate-900/90 border border-slate-700/80 hover:border-slate-600 focus:border-blue-500 text-slate-100 placeholder-slate-400 text-xs focus:outline-none transition shadow-inner" /> {searchTerm && ( )}
)} {/* Organized User Directory Dropdown (only when no recipient is locked in) */} {isDropdownOpen && !singleSelectedUser && (
{isLoading ? (
Loading tenant users directory...
) : groupedUsers.length === 0 ? (
No matching users found for "{searchTerm}".
) : ( groupedUsers.map(group => { const GroupIcon = group.icon; const isCollapsed = collapsedGroups[group.id]; return (
{/* Category Header */} {/* Users inside group */} {!isCollapsed && (
{group.users.map(u => { const selected = isUserSelected(u.id); const displayName = (u.email || '').split('@')[0].replace(/[._-]/g, ' '); const formattedName = displayName .split(' ') .map(w => w.charAt(0).toUpperCase() + w.slice(1)) .join(' '); return (
handleUserClick(u)} className={`flex items-center justify-between px-3 py-2 rounded-xl text-xs transition cursor-pointer border ${ selected ? 'bg-blue-600/20 border-blue-500/40 text-white' : 'bg-slate-900/40 hover:bg-slate-800/70 border-slate-800/40 text-slate-200' }`} >
{formattedName.slice(0, 2).toUpperCase()}

{formattedName}

{u.email}

{u.role && ( {u.role} )}
{selected && }
); })}
)}
); }) )} {/* Close button at bottom */}
)}
); };