import { Drawer, Flex, Form, Select, Typography, List, Button, Modal, Divider } from 'antd/es'; import { useTranslation } from 'react-i18next'; import { useEffect, useState } from 'react'; import { useAppSelector } from '@/hooks/useAppSelector'; import { useAppDispatch } from '@/hooks/useAppDispatch'; import { addProjectMember, createByEmail, deleteProjectMember, getAllProjectMembers, toggleProjectMemberDrawer, } from '@/features/projects/singleProject/members/projectMembersSlice'; import SingleAvatar from '@/components/common/single-avatar/single-avatar'; import { DeleteOutlined, MailOutlined } from '@/shared/antd-imports'; import { getTeamMembers } from '@/features/team-members/team-members.slice'; import logger from '@/utils/errorLogger'; import { validateEmail } from '@/utils/validateEmail'; import { ITeamMembersViewModel } from '@/types/teamMembers/teamMembersViewModel.types'; import { teamMembersApiService } from '@/api/team-members/teamMembers.api.service'; const ProjectMemberDrawer = () => { const { t } = useTranslation('project-view/project-member-drawer'); const { isDrawerOpen, currentMembersList, isLoading, isFromAssigner } = useAppSelector( state => state.projectMemberReducer ); const { projectId } = useAppSelector(state => state.projectReducer); const dispatch = useAppDispatch(); const [form] = Form.useForm(); const [searchTerm, setSearchTerm] = useState(''); const [isInviting, setIsInviting] = useState(false); const [members, setMembers] = useState({ data: [], total: 0 }); const [teamMembersLoading, setTeamMembersLoading] = useState(false); // Filter out members already in the project const currentProjectMemberIds = (currentMembersList || []).map(m => m.team_member_id).filter(Boolean); const availableMembers = (members?.data || []).filter( member => member.id && !currentProjectMemberIds.includes(member.id) ); const fetchProjectMembers = async () => { if (!projectId) return; dispatch(getAllProjectMembers(projectId)); }; const handleSearch = (value: string) => { setSearchTerm(value); }; const fetchTeamMembers = async () => { if (!searchTerm.trim()) return; try { setTeamMembersLoading(true); const response = await teamMembersApiService.get(1, 10, null, null, searchTerm, true); if (response.done) { setMembers(response.body); } } catch (error) { logger.error('Error fetching team members:', error); } finally { setTeamMembersLoading(false); } }; useEffect(() => { const handler = setTimeout(async () => { if (searchTerm.trim()) { fetchTeamMembers(); } }, 100); return () => { clearTimeout(handler); }; }, [searchTerm, dispatch]); const handleSelectChange = async (memberId: string) => { if (!projectId || !memberId) return; try { const res = await dispatch(addProjectMember({ memberId, projectId })).unwrap(); if (res.done) { form.resetFields(); dispatch( getTeamMembers({ index: 1, size: 5, field: null, order: null, search: null, all: true, }) ); await fetchProjectMembers(); } } catch (error) { logger.error('Error adding member:', error); } }; const handleDeleteMember = async (memberId: string | undefined) => { if (!memberId || !projectId) return; try { const res = await dispatch(deleteProjectMember({ memberId, projectId })).unwrap(); if (res.done) { await fetchProjectMembers(); } } catch (error) { logger.error('Error deleting member:', error); } }; const handleOpenChange = () => { if (isDrawerOpen) { fetchProjectMembers(); dispatch( getTeamMembers({ index: 1, size: 5, field: null, order: null, search: null, all: true, }) ); } }; const sendInviteToProject = async () => { if (!validateEmail(searchTerm) || !projectId) return; if (typeof searchTerm !== 'string' || !searchTerm.length) return; try { const email = searchTerm.trim().toLowerCase(); const body = { email, project_id: projectId, }; setIsInviting(true); const res = await dispatch(createByEmail(body)).unwrap(); if (res.done) { form.resetFields(); await fetchProjectMembers(); dispatch( getTeamMembers({ index: 1, size: 5, field: null, order: null, search: null, all: true, }) ); } } catch (error) { logger.error('Error sending invite:', error); } finally { setIsInviting(false); setSearchTerm(''); } }; const handleKeyDown = (event: React.KeyboardEvent) => { if (event.key === 'Enter') { sendInviteToProject(); } }; const renderMemberOption = (member: any) => ( {member.name} {member.email} ); const renderNotFoundContent = () => ( {/* {isFromAssigner && } */} ); return ( {isFromAssigner ? t('inviteMember') : t('title')} } open={isDrawerOpen} onCancel={() => dispatch(toggleProjectMemberDrawer())} afterOpenChange={handleOpenChange} footer={ <> {/* {!isFromAssigner && } */} } >