|
@@ -22,42 +22,103 @@ import RadioSelector from "components/RadioSelector";
|
|
|
|
|
|
|
|
type Props = {};
|
|
type Props = {};
|
|
|
|
|
|
|
|
|
|
+export type Collaborator = {
|
|
|
|
|
+ id: string;
|
|
|
|
|
+ user_id: string;
|
|
|
|
|
+ project_id: string;
|
|
|
|
|
+ email: string;
|
|
|
|
|
+ kind: string;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
const InvitePage: React.FunctionComponent<Props> = ({}) => {
|
|
const InvitePage: React.FunctionComponent<Props> = ({}) => {
|
|
|
- const { currentProject } = useContext(Context);
|
|
|
|
|
|
|
+ const { currentProject, setCurrentModal, user } = useContext(Context);
|
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
|
const [invites, setInvites] = useState<Array<InviteType>>([]);
|
|
const [invites, setInvites] = useState<Array<InviteType>>([]);
|
|
|
const [email, setEmail] = useState("");
|
|
const [email, setEmail] = useState("");
|
|
|
- const [role, setRole] = useState("viewer");
|
|
|
|
|
|
|
+ const [role, setRole] = useState("developer");
|
|
|
|
|
+ const [roleList, setRoleList] = useState([]);
|
|
|
const [isInvalidEmail, setIsInvalidEmail] = useState(false);
|
|
const [isInvalidEmail, setIsInvalidEmail] = useState(false);
|
|
|
const [isHTTPS] = useState(() => window.location.protocol === "https:");
|
|
const [isHTTPS] = useState(() => window.location.protocol === "https:");
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
- getInviteData();
|
|
|
|
|
- }, []);
|
|
|
|
|
|
|
+ api
|
|
|
|
|
+ .getAvailableRoles("<token>", {}, { project_id: currentProject.id })
|
|
|
|
|
+ .then(({ data }: { data: string[] }) => {
|
|
|
|
|
+ const availableRoleList = data?.map((role) => ({
|
|
|
|
|
+ value: role,
|
|
|
|
|
+ label: capitalizeFirstLetter(role),
|
|
|
|
|
+ }));
|
|
|
|
|
+ setRoleList(availableRoleList);
|
|
|
|
|
+ setRole("developer");
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ getData();
|
|
|
|
|
+ }, [currentProject]);
|
|
|
|
|
+
|
|
|
|
|
+ const capitalizeFirstLetter = (string: string) => {
|
|
|
|
|
+ return string.charAt(0).toUpperCase() + string.slice(1);
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- const getInviteData = () => {
|
|
|
|
|
|
|
+ const getData = async () => {
|
|
|
setIsLoading(true);
|
|
setIsLoading(true);
|
|
|
-
|
|
|
|
|
- api
|
|
|
|
|
- .getInvites(
|
|
|
|
|
|
|
+ let invites = [];
|
|
|
|
|
+ try {
|
|
|
|
|
+ const response = await api.getInvites(
|
|
|
"<token>",
|
|
"<token>",
|
|
|
{},
|
|
{},
|
|
|
{
|
|
{
|
|
|
id: currentProject.id,
|
|
id: currentProject.id,
|
|
|
}
|
|
}
|
|
|
- )
|
|
|
|
|
- .then((res) => {
|
|
|
|
|
- setInvites(res.data);
|
|
|
|
|
- setIsLoading(false);
|
|
|
|
|
- })
|
|
|
|
|
- .catch((err) => console.log(err));
|
|
|
|
|
|
|
+ );
|
|
|
|
|
+ invites = response.data.filter((i: InviteType) => !i.accepted);
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ console.log(err);
|
|
|
|
|
+ }
|
|
|
|
|
+ let collaborators: any = [];
|
|
|
|
|
+ try {
|
|
|
|
|
+ const response = await api.getCollaborators(
|
|
|
|
|
+ "<token>",
|
|
|
|
|
+ {},
|
|
|
|
|
+ {
|
|
|
|
|
+ project_id: currentProject.id,
|
|
|
|
|
+ }
|
|
|
|
|
+ );
|
|
|
|
|
+ collaborators = parseCollaboratorsResponse(response.data);
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ console.log(err);
|
|
|
|
|
+ }
|
|
|
|
|
+ setInvites([...invites, ...collaborators]);
|
|
|
|
|
+ setIsLoading(false);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const parseCollaboratorsResponse = (
|
|
|
|
|
+ collaborators: Array<Collaborator>
|
|
|
|
|
+ ): Array<InviteType> => {
|
|
|
|
|
+ return (
|
|
|
|
|
+ collaborators
|
|
|
|
|
+ // Parse role id to number
|
|
|
|
|
+ .map((c) => ({ ...c, id: Number(c.id) }))
|
|
|
|
|
+ // Sort them so the owner will be first allways
|
|
|
|
|
+ .sort((curr, prev) => curr.id - prev.id)
|
|
|
|
|
+ // Remove the owner from list
|
|
|
|
|
+ .slice(1)
|
|
|
|
|
+ // Parse the remainings to InviteType
|
|
|
|
|
+ .map((c) => ({
|
|
|
|
|
+ email: c.email,
|
|
|
|
|
+ expired: false,
|
|
|
|
|
+ id: Number(c.user_id),
|
|
|
|
|
+ kind: c.kind,
|
|
|
|
|
+ accepted: true,
|
|
|
|
|
+ token: "",
|
|
|
|
|
+ }))
|
|
|
|
|
+ );
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const createInvite = () => {
|
|
const createInvite = () => {
|
|
|
api
|
|
api
|
|
|
.createInvite("<token>", { email, kind: role }, { id: currentProject.id })
|
|
.createInvite("<token>", { email, kind: role }, { id: currentProject.id })
|
|
|
.then(() => {
|
|
.then(() => {
|
|
|
- getInviteData();
|
|
|
|
|
|
|
+ getData();
|
|
|
setEmail("");
|
|
setEmail("");
|
|
|
})
|
|
})
|
|
|
.catch((err) => console.log(err));
|
|
.catch((err) => console.log(err));
|
|
@@ -73,7 +134,7 @@ const InvitePage: React.FunctionComponent<Props> = ({}) => {
|
|
|
invId: inviteId,
|
|
invId: inviteId,
|
|
|
}
|
|
}
|
|
|
)
|
|
)
|
|
|
- .then(getInviteData)
|
|
|
|
|
|
|
+ .then(getData)
|
|
|
.catch((err) => console.log(err));
|
|
.catch((err) => console.log(err));
|
|
|
};
|
|
};
|
|
|
|
|
|
|
@@ -98,7 +159,7 @@ const InvitePage: React.FunctionComponent<Props> = ({}) => {
|
|
|
}
|
|
}
|
|
|
)
|
|
)
|
|
|
)
|
|
)
|
|
|
- .then(getInviteData)
|
|
|
|
|
|
|
+ .then(getData)
|
|
|
.catch((err) => console.log(err));
|
|
.catch((err) => console.log(err));
|
|
|
};
|
|
};
|
|
|
|
|
|
|
@@ -113,6 +174,30 @@ const InvitePage: React.FunctionComponent<Props> = ({}) => {
|
|
|
createInvite();
|
|
createInvite();
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+ const openEditModal = (user: any) => {
|
|
|
|
|
+ if (setCurrentModal) {
|
|
|
|
|
+ console.log(user);
|
|
|
|
|
+ setCurrentModal("EditInviteOrCollaboratorModal", {
|
|
|
|
|
+ user,
|
|
|
|
|
+ isInvite: user.status !== "accepted",
|
|
|
|
|
+ refetchCallerData: getData,
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const removeCollaborator = (user_id: number) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ api.removeCollaborator(
|
|
|
|
|
+ "<token>",
|
|
|
|
|
+ {},
|
|
|
|
|
+ { project_id: currentProject.id, user_id }
|
|
|
|
|
+ );
|
|
|
|
|
+ getData();
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.log(error);
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
const columns = useMemo<
|
|
const columns = useMemo<
|
|
|
Column<{
|
|
Column<{
|
|
|
email: string;
|
|
email: string;
|
|
@@ -181,21 +266,35 @@ const InvitePage: React.FunctionComponent<Props> = ({}) => {
|
|
|
{
|
|
{
|
|
|
id: "edit_action",
|
|
id: "edit_action",
|
|
|
Cell: ({ row }: any) => {
|
|
Cell: ({ row }: any) => {
|
|
|
- if (row.values.status === "accepted") {
|
|
|
|
|
- return <CopyButton>Edit</CopyButton>;
|
|
|
|
|
- }
|
|
|
|
|
- return null;
|
|
|
|
|
|
|
+ return (
|
|
|
|
|
+ <CopyButton
|
|
|
|
|
+ invis={row.original.currentUser}
|
|
|
|
|
+ onClick={() => openEditModal(row.original)}
|
|
|
|
|
+ >
|
|
|
|
|
+ Edit
|
|
|
|
|
+ </CopyButton>
|
|
|
|
|
+ );
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|
|
|
id: "remove_invite_action",
|
|
id: "remove_invite_action",
|
|
|
Cell: ({ row }: any) => {
|
|
Cell: ({ row }: any) => {
|
|
|
if (row.values.status === "accepted") {
|
|
if (row.values.status === "accepted") {
|
|
|
- return <CopyButton invis={true}>Remove</CopyButton>;
|
|
|
|
|
|
|
+ return (
|
|
|
|
|
+ <CopyButton
|
|
|
|
|
+ invis={row.original.currentUser}
|
|
|
|
|
+ onClick={() => removeCollaborator(row.original.id)}
|
|
|
|
|
+ >
|
|
|
|
|
+ Remove
|
|
|
|
|
+ </CopyButton>
|
|
|
|
|
+ );
|
|
|
}
|
|
}
|
|
|
return (
|
|
return (
|
|
|
<>
|
|
<>
|
|
|
- <CopyButton onClick={() => deleteInvite(row.values.id)}>
|
|
|
|
|
|
|
+ <CopyButton
|
|
|
|
|
+ invis={row.original.currentUser}
|
|
|
|
|
+ onClick={() => deleteInvite(row.original.id)}
|
|
|
|
|
+ >
|
|
|
Delete Invite
|
|
Delete Invite
|
|
|
</CopyButton>
|
|
</CopyButton>
|
|
|
</>
|
|
</>
|
|
@@ -218,10 +317,13 @@ const InvitePage: React.FunctionComponent<Props> = ({}) => {
|
|
|
|
|
|
|
|
const mappedInviteList = inviteList.map(
|
|
const mappedInviteList = inviteList.map(
|
|
|
({ accepted, expired, token, ...rest }) => {
|
|
({ accepted, expired, token, ...rest }) => {
|
|
|
|
|
+ const currentUser: boolean = user.email === rest.email;
|
|
|
|
|
+ console.log(currentUser, user, rest);
|
|
|
if (accepted) {
|
|
if (accepted) {
|
|
|
return {
|
|
return {
|
|
|
status: "accepted",
|
|
status: "accepted",
|
|
|
invite_link: buildInviteLink(token),
|
|
invite_link: buildInviteLink(token),
|
|
|
|
|
+ currentUser,
|
|
|
...rest,
|
|
...rest,
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
@@ -230,6 +332,7 @@ const InvitePage: React.FunctionComponent<Props> = ({}) => {
|
|
|
return {
|
|
return {
|
|
|
status: "expired",
|
|
status: "expired",
|
|
|
invite_link: buildInviteLink(token),
|
|
invite_link: buildInviteLink(token),
|
|
|
|
|
+ currentUser,
|
|
|
...rest,
|
|
...rest,
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
@@ -237,13 +340,14 @@ const InvitePage: React.FunctionComponent<Props> = ({}) => {
|
|
|
return {
|
|
return {
|
|
|
status: "pending",
|
|
status: "pending",
|
|
|
invite_link: buildInviteLink(token),
|
|
invite_link: buildInviteLink(token),
|
|
|
|
|
+ currentUser,
|
|
|
...rest,
|
|
...rest,
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
return mappedInviteList || [];
|
|
return mappedInviteList || [];
|
|
|
- }, [invites, currentProject?.id, window?.location?.host, isHTTPS]);
|
|
|
|
|
|
|
+ }, [invites, currentProject?.id, window?.location?.host, isHTTPS, user?.id]);
|
|
|
|
|
|
|
|
return (
|
|
return (
|
|
|
<>
|
|
<>
|
|
@@ -263,11 +367,7 @@ const InvitePage: React.FunctionComponent<Props> = ({}) => {
|
|
|
<RadioSelector
|
|
<RadioSelector
|
|
|
selected={role}
|
|
selected={role}
|
|
|
setSelected={setRole}
|
|
setSelected={setRole}
|
|
|
- options={[
|
|
|
|
|
- { value: "admin", label: "Admin" },
|
|
|
|
|
- { value: "developer", label: "Developer" },
|
|
|
|
|
- { value: "viewer", label: "Viewer" },
|
|
|
|
|
- ]}
|
|
|
|
|
|
|
+ options={roleList}
|
|
|
/>
|
|
/>
|
|
|
</RoleSelectorWrapper>
|
|
</RoleSelectorWrapper>
|
|
|
<ButtonWrapper>
|
|
<ButtonWrapper>
|