import { useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { Stack, Text, Button, Table, ActionIcon, Badge, Loader, Group, Modal, Select, CloseButton, Tooltip, Menu, Avatar, Box, } from '@mantine/core'; import LocalIcon from '@app/components/shared/LocalIcon'; import { alert } from '@app/components/toast'; import { teamService, Team } from '@app/services/teamService'; import { User, userManagementService } from '@app/services/userManagementService'; import { Z_INDEX_OVER_CONFIG_MODAL } from '@app/styles/zIndex'; interface TeamDetailsSectionProps { teamId: number; onBack: () => void; } export default function TeamDetailsSection({ teamId, onBack }: TeamDetailsSectionProps) { const { t } = useTranslation(); const [loading, setLoading] = useState(true); const [team, setTeam] = useState(null); const [teamUsers, setTeamUsers] = useState([]); const [availableUsers, setAvailableUsers] = useState([]); const [allTeams, setAllTeams] = useState([]); const [userLastRequest, setUserLastRequest] = useState>({}); const [addMemberModalOpened, setAddMemberModalOpened] = useState(false); const [changeTeamModalOpened, setChangeTeamModalOpened] = useState(false); const [selectedUser, setSelectedUser] = useState(null); const [selectedUserId, setSelectedUserId] = useState(''); const [selectedTeamId, setSelectedTeamId] = useState(''); const [processing, setProcessing] = useState(false); // License information const [licenseInfo, setLicenseInfo] = useState<{ availableSlots: number; } | null>(null); useEffect(() => { fetchTeamDetails(); fetchAllTeams(); }, [teamId]); const fetchTeamDetails = async () => { try { setLoading(true); const [data, adminData] = await Promise.all([ teamService.getTeamDetails(teamId), userManagementService.getUsers(), ]); console.log('[TeamDetailsSection] Raw data:', data); setTeam(data.team); setTeamUsers(Array.isArray(data.teamUsers) ? data.teamUsers : []); setAvailableUsers(Array.isArray(data.availableUsers) ? data.availableUsers : []); setUserLastRequest(data.userLastRequest || {}); // Store license information setLicenseInfo({ availableSlots: adminData.availableSlots, }); } catch (error) { console.error('Failed to fetch team details:', error); alert({ alertType: 'error', title: 'Failed to load team details' }); onBack(); } finally { setLoading(false); } }; const fetchAllTeams = async () => { try { const teams = await teamService.getTeams(); setAllTeams(teams); } catch (error) { console.error('Failed to fetch teams:', error); } }; const handleAddMember = async () => { if (!selectedUserId) { alert({ alertType: 'error', title: t('workspace.teams.addMemberToTeam.selectUserRequired', 'Please select a user') }); return; } try { setProcessing(true); await teamService.addUserToTeam(teamId, parseInt(selectedUserId)); alert({ alertType: 'success', title: t('workspace.teams.addMemberToTeam.success', 'User added to team successfully') }); setAddMemberModalOpened(false); setSelectedUserId(''); fetchTeamDetails(); } catch (error: any) { console.error('Failed to add member:', error); const errorMessage = error.response?.data?.message || error.response?.data?.error || error.message || t('workspace.teams.addMemberToTeam.error', 'Failed to add user to team'); alert({ alertType: 'error', title: errorMessage }); } finally { setProcessing(false); } }; const handleRemoveMember = async (user: User) => { if (!window.confirm(t('workspace.teams.confirmRemove', `Remove ${user.username} from this team?`))) { return; } try { setProcessing(true); // Find the Default team ID const defaultTeam = allTeams.find(t => t.name === 'Default'); if (!defaultTeam) { throw new Error('Default team not found'); } // Move user to Default team by updating their role with the Default team ID await teamService.moveUserToTeam(user.username, user.rolesAsString || 'ROLE_USER', defaultTeam.id); alert({ alertType: 'success', title: t('workspace.teams.removeMemberSuccess', 'User removed from team') }); fetchTeamDetails(); } catch (error: any) { console.error('Failed to remove member:', error); const errorMessage = error.response?.data?.message || error.response?.data?.error || error.message || t('workspace.teams.removeMemberError', 'Failed to remove user from team'); alert({ alertType: 'error', title: errorMessage }); } finally { setProcessing(false); } }; const handleDeleteUser = async (user: User) => { const confirmMessage = t('workspace.people.confirmDelete', 'Are you sure you want to delete this user? This action cannot be undone.'); if (!window.confirm(`${confirmMessage}\n\nUser: ${user.username}`)) { return; } try { setProcessing(true); await userManagementService.deleteUser(user.username); alert({ alertType: 'success', title: t('workspace.people.deleteUserSuccess', 'User deleted successfully') }); fetchTeamDetails(); } catch (error: any) { console.error('Failed to delete user:', error); const errorMessage = error.response?.data?.message || error.response?.data?.error || error.message || t('workspace.people.deleteUserError', 'Failed to delete user'); alert({ alertType: 'error', title: errorMessage }); } finally { setProcessing(false); } }; const openChangeTeamModal = (user: User) => { setSelectedUser(user); setSelectedTeamId(user.team?.id?.toString() || ''); setChangeTeamModalOpened(true); }; const handleChangeTeam = async () => { if (!selectedUser || !selectedTeamId) { alert({ alertType: 'error', title: t('workspace.teams.changeTeam.selectTeamRequired', 'Please select a team') }); return; } try { setProcessing(true); await teamService.moveUserToTeam(selectedUser.username, selectedUser.rolesAsString || 'ROLE_USER', parseInt(selectedTeamId)); alert({ alertType: 'success', title: t('workspace.teams.changeTeam.success', 'Team changed successfully') }); setChangeTeamModalOpened(false); setSelectedUser(null); setSelectedTeamId(''); fetchTeamDetails(); } catch (error: any) { console.error('Failed to change team:', error); const errorMessage = error.response?.data?.message || error.response?.data?.error || error.message || t('workspace.teams.changeTeam.error', 'Failed to change team'); alert({ alertType: 'error', title: errorMessage }); } finally { setProcessing(false); } }; if (loading) { return ( {t('workspace.teams.loadingDetails', 'Loading team details...')} ); } if (!team) { return ( {t('workspace.teams.teamNotFound', 'Team not found')} ); } return ( {/* Header with back button */}
{team.name} {t('workspace.teams.memberCount', { count: teamUsers.length })} {teamUsers.length === 1 ? 'member' : 'members'}
{/* Add Member Button */} 0} position="bottom" withArrow zIndex={Z_INDEX_OVER_CONFIG_MODAL} > {/* Members Table */} {t('workspace.people.user')} {t('workspace.people.role')} {teamUsers.length === 0 ? ( {t('workspace.teams.noMembers', 'No members in this team')} ) : ( teamUsers.map((user) => { const isActive = userLastRequest[user.username] && (Date.now() - userLastRequest[user.username]) < 5 * 60 * 1000; // Active within last 5 minutes return ( {user.username.charAt(0).toUpperCase()} {user.username} {user.email && ( {user.email} )} {(user.rolesAsString || '').includes('ROLE_ADMIN') ? t('workspace.people.admin') : t('workspace.people.member')} {/* Info icon with tooltip */} Authentication: {user.authenticationType || 'Unknown'} Last Activity:{' '} {userLastRequest[user.username] ? new Date(userLastRequest[user.username]).toLocaleString() : 'Never'} } multiline w={220} position="left" withArrow zIndex={Z_INDEX_OVER_CONFIG_MODAL + 10} > {/* Actions menu */} } onClick={() => openChangeTeamModal(user)} disabled={processing || team.name === 'Internal'} > {t('workspace.teams.changeTeam.label', 'Change Team')} {team.name !== 'Internal' && team.name !== 'Default' && ( } onClick={() => handleRemoveMember(user)} disabled={processing} > {t('workspace.teams.removeMember', 'Remove from team')} )} } onClick={() => handleDeleteUser(user)} disabled={processing || team.name === 'Internal'} > {t('workspace.people.deleteUser', 'Delete User')} ); }) )}
{/* Add Member Modal */} setAddMemberModalOpened(false)} size="md" zIndex={Z_INDEX_OVER_CONFIG_MODAL} centered padding="xl" withCloseButton={false} >
setAddMemberModalOpened(false)} size="lg" style={{ position: 'absolute', top: -8, right: -8, zIndex: 1, }} /> {/* Header with Icon */} {t('workspace.teams.addMemberToTeam.title')} {t('workspace.teams.addMemberToTeam.addingTo')} {team.name} t.name !== 'Internal') .map((team) => ({ value: team.id.toString(), label: team.name, }))} value={selectedTeamId} onChange={(value) => setSelectedTeamId(value || '')} searchable comboboxProps={{ withinPortal: true, zIndex: Z_INDEX_OVER_CONFIG_MODAL }} />
); }