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 (
+
+ );
+};
+
+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[];
}