import './InviteTeamMembers.styles.scss'; import { Color } from '@signozhq/design-tokens'; import { Button, Input, Select, Typography } from 'antd'; import { cloneDeep, debounce, isEmpty } from 'lodash-es'; import { ArrowLeft, ArrowRight, CheckCircle, Plus, TriangleAlert, } from 'lucide-react'; import { useCallback, useEffect, useState } from 'react'; import { v4 as uuid } from 'uuid'; interface TeamMember { email: string; role: string; name: string; frontendBaseUrl: string; id: string; } interface InviteTeamMembersProps { teamMembers: TeamMember[] | null; setTeamMembers: (teamMembers: TeamMember[]) => void; onNext: () => void; onBack: () => void; } function InviteTeamMembers({ teamMembers, setTeamMembers, onNext, onBack, }: InviteTeamMembersProps): JSX.Element { const [teamMembersToInvite, setTeamMembersToInvite] = useState< TeamMember[] | null >(teamMembers); const [emailValidity, setEmailValidity] = useState>( {}, ); const [hasInvalidEmails, setHasInvalidEmails] = useState(false); const defaultTeamMember: TeamMember = { email: '', role: 'EDITOR', name: '', frontendBaseUrl: '', id: '', }; useEffect(() => { if (isEmpty(teamMembers)) { const teamMember = { ...defaultTeamMember, id: uuid(), }; setTeamMembersToInvite([teamMember]); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [teamMembers]); const handleAddTeamMember = (): void => { const newTeamMember = { ...defaultTeamMember, id: uuid() }; setTeamMembersToInvite((prev) => [...(prev || []), newTeamMember]); }; // Validation function to check all users const validateAllUsers = (): boolean => { let isValid = true; const updatedValidity: Record = {}; teamMembersToInvite?.forEach((member) => { const emailValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(member.email); if (!emailValid || !member.email) { isValid = false; setHasInvalidEmails(true); } updatedValidity[member.id!] = emailValid; }); setEmailValidity(updatedValidity); return isValid; }; const handleNext = (): void => { if (validateAllUsers()) { setTeamMembers(teamMembersToInvite || []); onNext(); } }; // eslint-disable-next-line react-hooks/exhaustive-deps const debouncedValidateEmail = useCallback( debounce((email: string, memberId: string) => { const isValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); setEmailValidity((prev) => ({ ...prev, [memberId]: isValid })); }, 500), [], ); const handleEmailChange = ( e: React.ChangeEvent, member: TeamMember, ): void => { const { value } = e.target; const updatedMembers = cloneDeep(teamMembersToInvite || []); const memberToUpdate = updatedMembers.find((m) => m.id === member.id); if (memberToUpdate) { memberToUpdate.email = value; setTeamMembersToInvite(updatedMembers); debouncedValidateEmail(value, member.id!); } }; const handleRoleChange = (role: string, member: TeamMember): void => { const updatedMembers = cloneDeep(teamMembersToInvite || []); const memberToUpdate = updatedMembers.find((m) => m.id === member.id); if (memberToUpdate) { memberToUpdate.role = role; setTeamMembersToInvite(updatedMembers); } }; return (
Observability made collaborative The more your team uses SigNoz, the stronger your observability. Share dashboards, collaborate on alerts, and troubleshoot faster together.
Collaborate with your team
Invite your team to the SigNoz workspace
{teamMembersToInvite?.map((member) => (
): void => handleEmailChange(e, member) } addonAfter={ // eslint-disable-next-line no-nested-ternary emailValidity[member.id!] === undefined ? null : emailValidity[ member.id! ] ? ( ) : ( ) } />
))}
{hasInvalidEmails && (
Please enter valid emails for all team members
)}
); } export default InviteTeamMembers;