Implement functionality to add project members (#48)

* Add mutation for adding project member

* Add GQL client method for add project member

* Handle review comments

* Integrate add project member GQL client method

* Handle cascades for project members and user organizations

* Add null type to user.name and domain.redirectTo

---------

Co-authored-by: neeraj <neeraj.rtly@gmail.com>
This commit is contained in:
2024-02-01 15:34:17 +05:30
committed by GitHub
co-authored by neeraj
parent 7e2a313012
commit 0aa35d05f4
20 changed files with 252 additions and 113 deletions
@@ -1,5 +1,6 @@
import React, { useCallback } from 'react';
import { useForm } from 'react-hook-form';
import { AddProjectMemberInput, Permission } from 'gql-client';
import {
Button,
@@ -12,12 +13,10 @@ import {
Checkbox,
} from '@material-tailwind/react';
import { ProjectMember, Permission } from '../../../../types/project';
interface AddMemberDialogProp {
open: boolean;
handleOpen: () => void;
handleAddMember: (projectMember: ProjectMember) => void;
handleAddMember: (data: AddProjectMemberInput) => Promise<void>;
}
interface formData {
@@ -48,21 +47,17 @@ const AddMemberDialog = ({
},
});
const submitHandler = useCallback((data: formData) => {
const submitHandler = useCallback(async (data: formData) => {
reset();
handleOpen();
const projectMember: ProjectMember = {
id: Math.random().toString(),
permissions: [],
member: {
name: '',
email: data.emailAddress,
id: Math.random().toString(),
},
};
const permissions = Object.entries(data.permissions)
.filter(([, value]) => value)
.map(
([key]) => key.charAt(0).toUpperCase() + key.slice(1),
) as Permission[];
handleAddMember(projectMember);
await handleAddMember({ email: data.emailAddress, permissions });
}, []);
return (
@@ -96,13 +91,13 @@ const AddMemberDialog = ({
</Typography>
<Checkbox
crossOrigin={undefined}
label={Permission.VIEW}
label={Permission.View}
{...register(`permissions.view`)}
color="blue"
/>
<Checkbox
crossOrigin={undefined}
label={Permission.EDIT}
label={Permission.Edit}
{...register(`permissions.edit`)}
color="blue"
/>
@@ -1,5 +1,5 @@
import React, { useCallback, useState } from 'react';
import { Permission } from 'gql-client';
import { Permission, User } from 'gql-client';
import {
Select,
@@ -10,7 +10,6 @@ import {
} from '@material-tailwind/react';
import ConfirmDialog from '../../../shared/ConfirmDialog';
import { Member } from '../../../../types/project';
const PERMISSION_OPTIONS = [
{
@@ -29,12 +28,11 @@ const DROPDOWN_OPTIONS = [
];
interface MemberCardProps {
member: Member;
member: User;
isFirstCard: boolean;
isOwner: boolean;
isPending: boolean;
permissions: string[];
handleDeletePendingMember?: (id: string) => void;
onRemoveProjectMember?: () => Promise<void>;
onUpdateProjectMember?: (data: {
permissions: Permission[];
@@ -47,7 +45,6 @@ const MemberCard = ({
isOwner,
isPending,
permissions,
handleDeletePendingMember,
onRemoveProjectMember,
onUpdateProjectMember,
}: MemberCardProps) => {
@@ -82,7 +79,7 @@ const MemberCard = ({
>
<div>^</div>
<div className="basis-1/2">
<Typography variant="small">{member.name}</Typography>
{member.name && <Typography variant="small">{member.name}</Typography>}
<Typography variant="small">{member.email}</Typography>
</div>
<div className="basis-1/2">
@@ -122,9 +119,7 @@ const MemberCard = ({
size="sm"
className="rounded-full"
onClick={() => {
if (handleDeletePendingMember) {
handleDeletePendingMember(member.id);
}
setRemoveMemberDialogOpen((prevVal) => !prevVal);
}}
>
D
@@ -1,11 +1,15 @@
import React, { useCallback, useEffect, useState } from 'react';
import toast, { Toaster } from 'react-hot-toast';
import { Permission, Project } from 'gql-client';
import {
Permission,
Project,
AddProjectMemberInput,
ProjectMember,
} from 'gql-client';
import { Chip, Button, Typography } from '@material-tailwind/react';
import MemberCard from './MemberCard';
import { ProjectMember } from '../../../../types/project';
import AddMemberDialog from './AddMemberDialog';
import { useGQLClient } from '../../../../context/GQLClientContext';
@@ -18,17 +22,26 @@ const MembersTabPanel = ({ project }: { project: Project }) => {
const [projectMembers, setProjectMembers] = useState<ProjectMember[]>([]);
const addMemberHandler = useCallback((projectMember: ProjectMember) => {
setProjectMembers((val) => [...val, projectMember]);
toast.success('Invitation sent');
}, []);
const fetchProjectMembers = useCallback(async () => {
const { projectMembers } = await client.getProjectMembers(project.id);
setProjectMembers(projectMembers);
}, [project.id]);
const addMemberHandler = useCallback(
async (data: AddProjectMemberInput) => {
const { addProjectMember: isProjectMemberAdded } =
await client.addProjectMember(project.id, data);
if (isProjectMemberAdded) {
await fetchProjectMembers();
toast.success('Invitation sent');
} else {
toast.error('Invitation not sent');
}
},
[project],
);
const removeMemberHandler = async (projectMemberId: string) => {
const { removeProjectMember: isMemberRemoved } =
await client.removeProjectMember(projectMemberId);
@@ -96,15 +109,8 @@ const MembersTabPanel = ({ project }: { project: Project }) => {
key={projectMember.id}
isFirstCard={index === FIRST_MEMBER_CARD}
isOwner={projectMember.member.id === project.owner.id}
isPending={projectMember.member.name === ''}
isPending={projectMember.isPending}
permissions={projectMember.permissions}
handleDeletePendingMember={(id: string) => {
setProjectMembers(
projectMembers.filter(
(projectMember) => projectMember.member.id !== id,
),
);
}}
onRemoveProjectMember={async () =>
await removeMemberHandler(projectMember.id)
}