import { FC, ReactElement, useState, useEffect } from 'react';
import { Button } from '@imphnen-frontend-service/ui/atoms';
import { Link, useParams, useNavigate } from 'react-router';
import {
useTeamById,
useTeamMembers,
useInviteMember,
useTeamJoinRequests,
useRespondToJoinRequest,
useLeaveTeam,
useDeleteTeam,
ETeamMemberRole,
useAuthStore,
} from '@imphnen-frontend-service/service';
import { toast } from 'sonner';
import { Icon } from '@iconify/react';
const MAX_TEAM_MEMBERS = 5;
// Image component with loading state
const ImageWithLoader: FC<{
src: string;
alt: string;
className?: string;
onLoad?: () => void;
}> = ({ src, alt, className, onLoad }) => {
const [isLoaded, setIsLoaded] = useState(false);
return (
{!isLoaded && (
)}

{
setIsLoaded(true);
onLoad?.();
}}
/>
);
};
const TeamDashboardPage: FC = (): ReactElement => {
const { teamId } = useParams<{ teamId: string }>();
const navigate = useNavigate();
const { session } = useAuthStore();
const [showInviteModal, setShowInviteModal] = useState(false);
const [showJoinRequestsModal, setShowJoinRequestsModal] = useState(false);
const [showLeaveModal, setShowLeaveModal] = useState(false);
const [showDeleteModal, setShowDeleteModal] = useState(false);
const [deleteConfirmText, setDeleteConfirmText] = useState('');
const [inviteEmail, setInviteEmail] = useState('');
const [imagesLoaded, setImagesLoaded] = useState(false);
const [imageLoadCount, setImageLoadCount] = useState(0);
// Calculate total images to load (banner + logo + member avatars)
const totalImagesToLoad = 0; // Simplified - disable image loading overlay
const handleImageLoad = () => {
setImageLoadCount((prev) => {
const newCount = prev + 1;
if (newCount >= totalImagesToLoad) {
setImagesLoaded(true);
}
return newCount;
});
};
const handleImageError = () => {
// Treat error as loaded to not block the UI
handleImageLoad();
};
// Set images as loaded immediately since we disabled the loading overlay
useEffect(() => {
setImagesLoaded(true);
}, []);
const { data: teamData, isLoading: isLoadingTeam } = useTeamById(
teamId || ''
);
const { data: membersData, isLoading: isLoadingMembers } = useTeamMembers(
teamId || ''
);
const team = teamData?.data;
const members = membersData?.data || [];
const currentUserId = session?.user?.id;
const isLeader = currentUserId === team?.leader_id;
// Only fetch join requests if user is the team leader
const { data: joinRequestsData } = useTeamJoinRequests(
teamId || '',
!!teamId && isLeader
);
const { mutateAsync: inviteMember, isPending: isInviting } = useInviteMember(
teamId || ''
);
const { mutateAsync: respondToJoinRequest, isPending: isResponding } =
useRespondToJoinRequest(teamId || '');
const { mutateAsync: leaveTeam, isPending: isLeaving } = useLeaveTeam();
const { mutateAsync: deleteTeam, isPending: isDeleting } = useDeleteTeam();
const joinRequests = joinRequestsData?.data || [];
const pendingJoinRequests = joinRequests.filter(
(req: any) => req.status === 'pending'
);
const isMember = members.some(
(member: any) => member.user_id === currentUserId
);
const canInvite = isLeader && members.length < MAX_TEAM_MEMBERS;
const handleInviteMember = async (e: React.FormEvent) => {
e.preventDefault();
if (!inviteEmail.trim() || isInviting) return;
try {
await inviteMember({ email: inviteEmail.trim() });
toast.success('Invitation sent successfully!');
setInviteEmail('');
setShowInviteModal(false);
} catch (error) {
console.error('Failed to invite member:', error);
toast.error('Failed to send invitation');
}
};
const handleRespondToJoinRequest = async (
requestId: string,
action: 'approve' | 'reject'
) => {
try {
await respondToJoinRequest({ requestId, action });
toast.success(
action === 'approve' ? 'Request approved!' : 'Request rejected'
);
} catch (error) {
console.error('Failed to respond to join request:', error);
toast.error('Failed to process request');
}
};
const handleLeaveTeam = async () => {
if (!teamId) return;
try {
await leaveTeam(teamId);
toast.success('You have left the team');
navigate('/dashboard');
} catch (error) {
console.error('Failed to leave team:', error);
toast.error('Failed to leave team');
}
};
const handleDeleteTeam = async () => {
if (!teamId) return;
try {
await deleteTeam(teamId);
toast.success('Team deleted successfully');
navigate('/dashboard');
} catch (error: any) {
console.error('Failed to delete team:', error);
toast.error(error?.message || 'Failed to delete team');
}
};
if (isLoadingTeam || isLoadingMembers) {
return (
{/* Skeleton Header */}
{/* Main Content Skeleton */}
{/* Sidebar Skeleton */}
{[1, 2, 3].map((i) => (
))}
{/* Loading Overlay */}
);
}
if (!team) {
return (
Team not found
);
}
return (
{/* Loading overlay while images are loading */}
{!imagesLoaded && totalImagesToLoad > 0 && (
Loading images...
{imageLoadCount} / {totalImagesToLoad}
)}
{/* Header with Banner */}
{team.banner && (
)}
{team.logo && (
{!imagesLoaded && (
)}
)}
{team.name}
{team.city}
{members.length}{' '}
{members.length === 1 ? 'Member' : 'Members'}
{team.visibility === 'public' ? 'Public' : 'Private'}
{isLeader && (
Team Leader
)}
{/* Main Content */}
{/* Team Description */}
About Team
{team.description}
{/* Team Actions - Only for Leader */}
{isLeader && (
Team Management
{/* Hide invite/join management after submission */}
{!team.has_submission && (
<>
>
)}
{!team.has_submission && (
)}
{team.has_submission ? (
) : (
)}
{!team.has_submission && !canInvite && members.length >= MAX_TEAM_MEMBERS && (
Maximum team size reached ({MAX_TEAM_MEMBERS} members)
)}
{/* Danger Zone - Only show when leader is alone and no submission */}
{members.length === 1 && !team.has_submission && (
Danger Zone
)}
)}
{/* Quick Actions for Members (non-leaders) */}
{!isLeader && isMember && (
Quick Actions
{team.has_submission && (
)}
)}
{/* Warning for single member teams */}
{members.length === 1 && !team.has_submission && isMember && (
⚠️
Team Needs More Members
Your team needs at least 2 members to submit a project. Invite someone or wait for join requests!
)}
{/* Submission Status - Only show to members */}
{team.has_submission && isMember && (
✅
Project Submitted
Your team has successfully submitted a project
)}
{/* Sidebar */}
{/* Team Leader */}
Team Leader
{team.leader && (
)}
{/* Team Members */}
Members ({members.length})
{members.map((member: any) => (
))}
{/* Invite Member Modal */}
{showInviteModal && (
Invite Team Member
Send an invitation to join your team. The invited member will see
the invitation on their dashboard after logging in.
Important: The email you enter must match the
GitHub email address the member uses to sign in.
)}
{/* Join Requests Modal */}
{showJoinRequestsModal && (
Join Requests ({pendingJoinRequests.length})
{pendingJoinRequests.length === 0 ? (
No pending join requests
When users request to join your team, they'll appear here
) : (
{pendingJoinRequests.map((request: any) => (
{request.user?.avatar ? (

) : (
)}
{request.user?.fullname || 'Unknown User'}
{request.user?.email}
{request.message && (
Message: {request.message}
)}
Requested{' '}
{new Date(request.created_at).toLocaleDateString()}
{members.length >= MAX_TEAM_MEMBERS && (
Team is full ({MAX_TEAM_MEMBERS}/{MAX_TEAM_MEMBERS}{' '}
members). Remove a member before accepting new
requests.
)}
))}
)}
)}
{/* Leave Team Confirmation Modal */}
{showLeaveModal && (
Leave Team?
Are you sure you want to leave {team?.name}?
Warning: If you leave, you will need to request to join again or be re-invited by the team leader.
)}
{/* Delete Team Confirmation Modal */}
{showDeleteModal && (
Delete Team?
This action is permanent and cannot be undone.
Warning: All team data, chat messages, and submissions will be permanently deleted.
setDeleteConfirmText(e.target.value)}
placeholder="Type team name here"
className="w-full px-4 py-2.5 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-red-500 focus:border-transparent bg-white dark:bg-gray-800 text-gray-900 dark:text-white placeholder:text-gray-400 dark:placeholder:text-gray-500"
autoComplete="off"
/>
)}
);
};
export default TeamDashboardPage;