import React, { useState, useMemo } from 'react'; import { Users, User, Shield, ShieldAlert, ShieldCheck, Mail, Trash2, Clock, Check, X, GitMerge, ArrowRight, Search, Plus, Sparkles } from 'lucide-react'; import { useTourStore } from '@/store/useTourStore'; import { useConfirm } from '@/hooks/useConfirm'; import { useNotification } from '@/hooks/useNotification'; interface MembersTabProps { tourId: string; participants: any[]; joinRequests: any[]; userRole: string | null; canManage: boolean; isOwner: boolean; onRemoveMember: (memberIdOrUserId: string) => Promise; onRefresh: () => void; onOpenAddMember?: () => void; } export const MembersTab: React.FC = ({ tourId, participants, joinRequests: initialJoinRequests, canManage, isOwner, onRemoveMember, onRefresh, onOpenAddMember }) => { const confirm = useConfirm(); const notify = useNotification(); const acceptJoinRequest = useTourStore(state => state.acceptJoinRequest); const rejectJoinRequest = useTourStore(state => state.rejectJoinRequest); const [joinRequests, setJoinRequests] = useState(initialJoinRequests); const [mergingId, setMergingId] = useState(null); const [joinRequestActionId, setJoinRequestActionId] = useState(null); const [assigningManualMember, setAssigningManualMember] = useState(null); const [systemSearchQuery, setSystemSearchQuery] = useState(''); // Sync state with props React.useEffect(() => { setJoinRequests(initialJoinRequests); }, [initialJoinRequests]); // Separate system vs manual members const systemMembers = useMemo(() => { return participants.filter((p: any) => p.userId && p.user); }, [participants]); const manualMembers = useMemo(() => { return participants.filter((p: any) => !p.userId && p.displayName); }, [participants]); // Auto-detect duplicate matches based on case-insensitive names const duplicateMatches = useMemo(() => { const matches: Array<{ manual: any; system: any }> = []; manualMembers.forEach((m: any) => { const match = systemMembers.find((s: any) => { return s.user.name.trim().toLowerCase() === m.displayName.trim().toLowerCase(); }); if (match) { matches.push({ manual: m, system: match }); } }); return matches; }, [systemMembers, manualMembers]); // Filter system members for manual merge modal const filteredSystemMembersForMerge = useMemo(() => { if (!systemSearchQuery.trim()) return systemMembers; return systemMembers.filter((s: any) => s.user.name.toLowerCase().includes(systemSearchQuery.toLowerCase()) || (s.user.email && s.user.email.toLowerCase().includes(systemSearchQuery.toLowerCase())) ); }, [systemMembers, systemSearchQuery]); // Handle merging logic const handleMerge = async (manualParticipantId: string, systemUserId: string, manualName: string, systemName: string) => { const isConfirmed = await confirm({ title: 'Hợp nhất thành viên', message: `Bạn có chắc muốn hợp nhất thành viên thủ công "${manualName}" vào tài khoản "${systemName}"? Bản ghi thủ công sẽ bị xóa và các dữ liệu liên quan sẽ được gộp.` }); if (!isConfirmed) return; setMergingId(manualParticipantId); try { const res = await fetch(`/api/v1/tours/${tourId}/members/merge`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('token')}` }, body: JSON.stringify({ manualParticipantId, systemUserId }) }); const data = await res.json(); if (!res.ok) { throw new Error(data.message || 'Hợp nhất thất bại.'); } notify({ title: 'Thành công', message: 'Hợp nhất thành viên thành công!', type: 'success' }); setAssigningManualMember(null); onRefresh(); } catch (e: any) { notify({ title: 'Lỗi', message: e.message || 'Không thể hợp nhất', type: 'error' }); } finally { setMergingId(null); } }; const getRoleBadge = (role: string) => { switch (role) { case 'OWNER': return ( Trưởng đoàn ); case 'MANAGER': return ( Phó đoàn ); case 'MEMBER': return ( Thành viên ); case 'MEMBER_NO_FINANCE': return ( Thành viên (Không xem quỹ) ); default: return ( {role} ); } }; // Get current user id to prevent self-deletion const getCurrentUserId = () => { const rawToken = localStorage.getItem('token'); if (!rawToken) return null; try { const payload = JSON.parse(atob(rawToken.split('.')[1])); return payload.sub; } catch { return null; } }; const currentUserId = getCurrentUserId(); return (
{/* Header and Actions */}

Quản lý thành viên

Quản lý thành viên hệ thống, thành viên thủ công và các yêu cầu tham gia chuyến đi.

{canManage && onOpenAddMember && ( )}
{/* Auto-detect duplicates alert */} {duplicateMatches.length > 0 && (

Phát hiện trùng lặp tự động

Hệ thống phát hiện có thành viên được tạo thủ công trùng tên với tài khoản hệ thống mới gia nhập. Bạn nên gộp họ lại để đồng bộ thông tin chặng đi và chi phí.

{duplicateMatches.map((match) => (
Thành viên thủ công: "{match.manual.displayName}" Tài khoản hệ thống: "{match.system.user.name}"
))}
)} {/* Pending requests */} {joinRequests.length > 0 && (

Yêu cầu tham gia chờ duyệt

{joinRequests.length}
{joinRequests.map((req: any) => (
{req.user?.name?.charAt(0) || '?'}
{req.user?.name || req.userId}
{req.user?.email}
{isOwner && (
)}
))}
)} {/* Main Members Grid */}
{/* System Accounts */}

Thành viên hệ thống ({systemMembers.length})

{systemMembers.map((member: any) => { const isCurrentUser = currentUserId && member.userId === currentUserId; const isMemberOwner = member.role === 'OWNER'; const canRemove = canManage && !isCurrentUser && !isMemberOwner; return (
{member.user.name.charAt(0)}
{member.user.name} {isCurrentUser && Tôi}
{member.user.email}
{getRoleBadge(member.role)} {canRemove && ( )}
); })}
{/* Manual Members */}

Thành viên thủ công ({manualMembers.length})

{manualMembers.map((member: any) => { const canRemove = canManage; return (
{member.displayName.charAt(0)}
{member.displayName}
Tạo ngoài hệ thống
{getRoleBadge(member.role)} {canManage && ( )} {canRemove && ( )}
); })} {manualMembers.length === 0 && (
Chưa có thành viên thủ công nào
)}
{/* Manual Merge Modal Selector */} {assigningManualMember && (
setAssigningManualMember(null)} />

Gán tài khoản hệ thống

Chọn một tài khoản hệ thống để gán cho thành viên thủ công "{assigningManualMember.displayName}".

{/* Search Bar */}
setSystemSearchQuery(e.target.value)} className="w-full pl-9 pr-4 py-2 border border-gray-200 rounded-xl text-xs outline-none focus:border-indigo-500 focus:ring-2 focus:ring-indigo-100 transition-all" />
{/* System accounts list */}
{filteredSystemMembersForMerge.map((systemMember: any) => ( ))} {filteredSystemMembersForMerge.length === 0 && (
Không tìm thấy tài khoản hệ thống phù hợp.
)}
)}
); };