({
organizationId,
userRole,
noCardWrapper,
}: TeamManagementProps)
| 75 | } |
| 76 | |
| 77 | export function TeamManagement({ |
| 78 | organizationId, |
| 79 | userRole, |
| 80 | noCardWrapper, |
| 81 | }: TeamManagementProps) { |
| 82 | const [members, setMembers] = useState<Member[]>([]) |
| 83 | const [invitations, setInvitations] = useState<Invitation[]>([]) |
| 84 | const [loading, setLoading] = useState(true) |
| 85 | const [hasInitiallyLoaded, setHasInitiallyLoaded] = useState(false) |
| 86 | const [inviteDialogOpen, setInviteDialogOpen] = useState(false) |
| 87 | const [bulkInviteDialogOpen, setBulkInviteDialogOpen] = useState(false) |
| 88 | const [inviteForm, setInviteForm] = useState({ |
| 89 | email: '', |
| 90 | role: 'member' as 'admin' | 'member', |
| 91 | }) |
| 92 | const [bulkInviteForm, setBulkInviteForm] = useState({ |
| 93 | emails: '', |
| 94 | role: 'member' as 'admin' | 'member', |
| 95 | }) |
| 96 | const [inviting, setInviting] = useState(false) |
| 97 | const [bulkInviting, setBulkInviting] = useState(false) |
| 98 | const [resendingInvites, setResendingInvites] = useState<Set<string>>( |
| 99 | new Set(), |
| 100 | ) |
| 101 | const [_refreshing, setRefreshing] = useState(false) |
| 102 | const [confirmResendDialogOpen, setConfirmResendDialogOpen] = useState(false) |
| 103 | const [currentInvitationToResend, setCurrentInvitationToResend] = |
| 104 | useState<Invitation | null>(null) |
| 105 | const [confirmCancelDialogOpen, setConfirmCancelDialogOpen] = useState(false) |
| 106 | const [currentInvitationToCancel, setCurrentInvitationToCancel] = |
| 107 | useState<Invitation | null>(null) |
| 108 | const [confirmRemoveDialogOpen, setConfirmRemoveDialogOpen] = useState(false) |
| 109 | const [currentMemberToRemove, setCurrentMemberToRemove] = useState<{ |
| 110 | userId: string |
| 111 | name: string |
| 112 | } | null>(null) |
| 113 | |
| 114 | const canManageTeam = userRole === 'owner' || userRole === 'admin' |
| 115 | const isMobile = useIsMobile() |
| 116 | const hasMountedRef = useRef(false) |
| 117 | const searchParams = useSearchParams() ?? new URLSearchParams() |
| 118 | |
| 119 | // Auto-open invite dialog if invite=true query param is present |
| 120 | useEffect(() => { |
| 121 | if (searchParams.get('invite') === 'true' && canManageTeam) { |
| 122 | setBulkInviteDialogOpen(true) |
| 123 | } |
| 124 | }, [searchParams, canManageTeam]) |
| 125 | |
| 126 | useEffect(() => { |
| 127 | // Only show loading skeleton on initial mount, not on subsequent mounts |
| 128 | if (!hasMountedRef.current) { |
| 129 | hasMountedRef.current = true |
| 130 | fetchTeamData(true) // true = initial load |
| 131 | } else if (hasInitiallyLoaded) { |
| 132 | // If we've loaded before, just refresh without showing skeleton |
| 133 | fetchTeamData(false) // false = refresh load |
| 134 | } |
nothing calls this directly
no test coverage detected