diff --git a/packages/frontend/src/assets/members.json b/packages/frontend/src/assets/members.json index c8c7cb0e..6e38602c 100644 --- a/packages/frontend/src/assets/members.json +++ b/packages/frontend/src/assets/members.json @@ -2,19 +2,16 @@ { "name": "Saugat Yadav", "email": "saugaty@airfoil.studio", - "id": 1, - "permissions": [] + "id": 1 }, { "name": "Gideon Low", "email": "gideonl@airfoil.studio", - "id": 2, - "permissions": ["view", "edit"] + "id": 2 }, { "name": "Sushan Yadav", "email": "sushany@airfoil.studio", - "id": 3, - "permissions": ["view"] + "id": 3 } ] diff --git a/packages/frontend/src/assets/projects.json b/packages/frontend/src/assets/projects.json index 091df143..a344fdf7 100644 --- a/packages/frontend/src/assets/projects.json +++ b/packages/frontend/src/assets/projects.json @@ -17,7 +17,20 @@ "branch": "main" }, "repositoryId": 1, - "members": [1, 2, 3], + "members": [ + { + "id": 1, + "permissions": [] + }, + { + "id": 2, + "permissions": ["view", "edit"] + }, + { + "id": 3, + "permissions": ["view"] + } + ], "ownerId": 1 }, { @@ -38,7 +51,16 @@ "branch": "staging" }, "repositoryId": 1, - "members": [2, 3], + "members": [ + { + "id": 2, + "permissions": [] + }, + { + "id": 3, + "permissions": ["view"] + } + ], "ownerId": 2 }, { @@ -59,7 +81,20 @@ "branch": "main" }, "repositoryId": 1, - "members": [1], + "members": [ + { + "id": 1, + "permissions": [] + }, + { + "id": 2, + "permissions": ["view", "edit"] + }, + { + "id": 3, + "permissions": ["view"] + } + ], "ownerId": 1 }, { @@ -80,7 +115,20 @@ "branch": "main" }, "repositoryId": 1, - "members": [1], + "members": [ + { + "id": 1, + "permissions": [] + }, + { + "id": 2, + "permissions": ["view", "edit"] + }, + { + "id": 3, + "permissions": ["view"] + } + ], "ownerId": 1 }, { @@ -101,8 +149,13 @@ "branch": "main" }, "repositoryId": 1, - "members": [1], - "ownerId": 1 + "members": [ + { + "id": 3, + "permissions": [] + } + ], + "ownerId": 3 }, { "id": 6, @@ -122,7 +175,16 @@ "branch": "prod" }, "repositoryId": 1, - "members": [1], - "ownerId": 1 + "members": [ + { + "id": 2, + "permissions": [] + }, + { + "id": 3, + "permissions": ["view"] + } + ], + "ownerId": 2 } ] diff --git a/packages/frontend/src/components/projects/project/settings/AddMemberDialog.tsx b/packages/frontend/src/components/projects/project/settings/AddMemberDialog.tsx new file mode 100644 index 00000000..9f79331b --- /dev/null +++ b/packages/frontend/src/components/projects/project/settings/AddMemberDialog.tsx @@ -0,0 +1,124 @@ +import React, { useCallback } from 'react'; +import { useForm } from 'react-hook-form'; + +import { + Button, + Dialog, + DialogHeader, + DialogBody, + DialogFooter, + Input, + Typography, + Checkbox, +} from '@material-tailwind/react'; + +import { Member, Permission } from '../../../../types/project'; + +interface AddMemberDialogProp { + open: boolean; + handleOpen: () => void; + handleAddMember: (member: Member) => void; +} + +interface formData { + emailAddress: string; + permissions: { + view: boolean; + edit: boolean; + }; +} + +const AddMemberDialog = ({ + open, + handleOpen, + handleAddMember, +}: AddMemberDialogProp) => { + const { + handleSubmit, + register, + reset, + formState: { isValid }, + } = useForm({ + defaultValues: { + emailAddress: '', + permissions: { + view: true, + edit: false, + }, + }, + }); + + const submitHandler = useCallback((data: formData) => { + reset(); + handleOpen(); + + const member: Member = { + email: data.emailAddress, + id: Math.random(), + name: '', + }; + + handleAddMember(member); + }, []); + + return ( + + +
Add member
+ +
+
+ + + We will send an invitation link to this email address. + + Email address + + Permissions + + You can change this later if required. + + + + + + + + +
+
+ ); +}; + +export default AddMemberDialog; diff --git a/packages/frontend/src/components/projects/project/settings/EditEnvironmentVariableRow.tsx b/packages/frontend/src/components/projects/project/settings/EditEnvironmentVariableRow.tsx index aec238d8..fadd91d3 100644 --- a/packages/frontend/src/components/projects/project/settings/EditEnvironmentVariableRow.tsx +++ b/packages/frontend/src/components/projects/project/settings/EditEnvironmentVariableRow.tsx @@ -131,7 +131,7 @@ const EditEnvironmentVariableRow = ({ color="red" > - Are you sure you want to delete the variable + Are you sure you want to delete the variable  {variable.key}? diff --git a/packages/frontend/src/components/projects/project/settings/GitTabPanel.tsx b/packages/frontend/src/components/projects/project/settings/GitTabPanel.tsx index 9f743bbe..16433f9c 100644 --- a/packages/frontend/src/components/projects/project/settings/GitTabPanel.tsx +++ b/packages/frontend/src/components/projects/project/settings/GitTabPanel.tsx @@ -100,7 +100,11 @@ const GitTabPanel = () => { )} Branch name - + diff --git a/packages/frontend/src/components/projects/project/settings/MemberCard.tsx b/packages/frontend/src/components/projects/project/settings/MemberCard.tsx index 702a9ba3..198a6027 100644 --- a/packages/frontend/src/components/projects/project/settings/MemberCard.tsx +++ b/packages/frontend/src/components/projects/project/settings/MemberCard.tsx @@ -1,6 +1,13 @@ import React, { useCallback, useState } from 'react'; +import toast from 'react-hot-toast'; -import { Select, Typography, Option } from '@material-tailwind/react'; +import { + Select, + Typography, + Option, + Chip, + IconButton, +} from '@material-tailwind/react'; import { Member } from '../../../../types/project'; import ConfirmDialog from '../../../shared/ConfirmDialog'; @@ -25,11 +32,21 @@ interface MemberCardProps { member: Member; isFirstCard: boolean; isOwner: boolean; + isPending: boolean; + permissions: string[]; + handleDeletePendingMember: (id: number) => void; } -const MemberCard = ({ member, isFirstCard, isOwner }: MemberCardProps) => { +const MemberCard = ({ + member, + isFirstCard, + isOwner, + isPending, + permissions, + handleDeletePendingMember, +}: MemberCardProps) => { const [selectedPermission, setSelectedPermission] = useState( - member.permissions.join('+'), + permissions.join('+'), ); const [removeMemberDialogOpen, setRemoveMemberDialogOpen] = useState(false); @@ -53,30 +70,55 @@ const MemberCard = ({ member, isFirstCard, isOwner }: MemberCardProps) => { className={`flex p-1 ${!isFirstCard && 'mt-1 border-t border-gray-300'}`} >
^
-
+
{member.name} {member.email}
-
- +
+ {!isPending ? ( + + ) : ( +
+
+ +
+
+ { + handleDeletePendingMember(member.id); + }} + > + D + +
+
+ )}
{ confirmButtonTitle="Yes, Remove member" handleConfirm={() => { setRemoveMemberDialogOpen((preVal) => !preVal); + toast.success('Member removed from project'); }} color="red" > diff --git a/packages/frontend/src/components/projects/project/settings/MembersTabPanel.tsx b/packages/frontend/src/components/projects/project/settings/MembersTabPanel.tsx index db2a2976..086cfc7c 100644 --- a/packages/frontend/src/components/projects/project/settings/MembersTabPanel.tsx +++ b/packages/frontend/src/components/projects/project/settings/MembersTabPanel.tsx @@ -1,5 +1,6 @@ -import React, { useMemo } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import { useParams } from 'react-router-dom'; +import toast, { Toaster } from 'react-hot-toast'; import { Chip, Button, Typography } from '@material-tailwind/react'; @@ -8,24 +9,44 @@ import membersData from '../../../../assets/members.json'; import projectData from '../../../../assets/projects.json'; import { Member } from '../../../../types/project'; +import AddMemberDialog from './AddMemberDialog'; const FIRST_MEMBER_CARD = 0; const MembersTabPanel = () => { const { id } = useParams(); + const [addmemberDialogOpen, setAddMemberDialogOpen] = useState(false); const currProject = useMemo(() => { return projectData.find((data) => data.id === Number(id)); }, [id]); const members = useMemo(() => { - return ( - currProject?.members.map((memberId) => { - return membersData.find((member) => member.id === memberId); - }) || [] + return membersData.filter( + (member) => + currProject?.members.some( + (projectMember) => projectMember.id === member.id, + ), ); }, [currProject]); + const [updatedMembers, setUpdatedMembers] = useState([...members]); + + const getMemberPermissions = useCallback( + (id: number) => { + return ( + currProject?.members.find((projectMember) => projectMember.id === id) + ?.permissions || [] + ); + }, + [updatedMembers], + ); + + const addMemberHandler = useCallback((member: Member) => { + setUpdatedMembers((val) => [...val, member]); + toast.success('Invitation sent'); + }, []); + return (
@@ -35,24 +56,44 @@ const MembersTabPanel = () => {
- +
- {(members as Member[]).map((member, index) => { + {(updatedMembers as Member[]).map((member, index) => { return ( { + setUpdatedMembers( + updatedMembers.filter((member) => member.id !== id), + ); + }} /> ); })} + { + setAddMemberDialogOpen((preVal) => !preVal); + }} + open={addmemberDialogOpen} + handleAddMember={addMemberHandler} + /> +
); }; diff --git a/packages/frontend/src/types/project.ts b/packages/frontend/src/types/project.ts index 924706e0..9c617afc 100644 --- a/packages/frontend/src/types/project.ts +++ b/packages/frontend/src/types/project.ts @@ -16,10 +16,15 @@ export interface ProjectDetails { branch: string; }; repositoryId: number; - members: number[]; + members: MemberPermission[]; ownerId: number; } +export interface MemberPermission { + id: number; + permissions: string[]; +} + export interface DeploymentDetails { title: string; isProduction: boolean; @@ -93,5 +98,4 @@ export interface Member { name: string; email: string; id: number; - permissions: Permission[]; }