Bläddra i källkod

Implemented role selection on invite

jnfrati 3 år sedan
förälder
incheckning
32d7bf76fa

+ 96 - 7
dashboard/src/main/home/project-settings/InviteList.tsx

@@ -13,6 +13,8 @@ import CopyToClipboard from "components/CopyToClipboard";
 import { Column } from "react-table";
 import Table from "components/Table";
 import RadioSelector from "components/RadioSelector";
+import { Role } from "./roles-admin/types";
+import SearchSelector from "components/SearchSelector";
 
 type Props = {};
 
@@ -22,6 +24,7 @@ export type Collaborator = {
   project_id: string;
   email: string;
   kind: string;
+  roles: string[];
 };
 
 const InvitePage: React.FunctionComponent<Props> = ({}) => {
@@ -42,6 +45,9 @@ const InvitePage: React.FunctionComponent<Props> = ({}) => {
   const [isInvalidEmail, setIsInvalidEmail] = useState(false);
   const [isHTTPS] = useState(() => window.location.protocol === "https:");
 
+  const [roles, setRoles] = useState<Role[]>([]);
+  const [selectedRoles, setSelectedRoles] = useState<Role[]>([]);
+
   useEffect(() => {
     api
       .getAvailableRoles("<token>", {}, { project_id: currentProject?.id })
@@ -54,6 +60,10 @@ const InvitePage: React.FunctionComponent<Props> = ({}) => {
         setRole("developer");
       });
 
+    api
+      .listRoles("<token>", {}, { project_id: currentProject?.id })
+      .then((res) => setRoles(res.data));
+
     getData();
   }, [currentProject]);
 
@@ -119,10 +129,15 @@ const InvitePage: React.FunctionComponent<Props> = ({}) => {
 
   const createInvite = () => {
     api
-      .createInvite("<token>", { email, kind: role }, { id: currentProject.id })
+      .createInvite(
+        "<token>",
+        { email, kind: role, roles: selectedRoles.map((role) => role.id) },
+        { id: currentProject.id }
+      )
       .then(() => {
         getData();
         setEmail("");
+        setSelectedRoles([]);
       })
       .catch((err) => {
         if (err.response.data?.error) {
@@ -150,12 +165,13 @@ const InvitePage: React.FunctionComponent<Props> = ({}) => {
   const replaceInvite = (
     inviteEmail: string,
     inviteId: number,
-    kind: string
+    kind: string,
+    roles: string[]
   ) => {
     api
       .createInvite(
         "<token>",
-        { email: inviteEmail, kind },
+        { email: inviteEmail, kind, roles },
         { id: currentProject.id }
       )
       .then(() =>
@@ -219,6 +235,7 @@ const InvitePage: React.FunctionComponent<Props> = ({}) => {
       status: string;
       invite_link: string;
       kind: string;
+      roles: string[];
     }>[]
   >(
     () => [
@@ -230,7 +247,7 @@ const InvitePage: React.FunctionComponent<Props> = ({}) => {
         Header: "Role",
         accessor: "kind",
         Cell: ({ row }) => {
-          return <Role>{row.values.kind || "Developer"}</Role>;
+          return <RoleName>{row.values.roles?.join(",") || "N/A"}</RoleName>;
         },
       },
       {
@@ -253,7 +270,8 @@ const InvitePage: React.FunctionComponent<Props> = ({}) => {
                   replaceInvite(
                     row.values.email,
                     row.original.id,
-                    row.values.kind
+                    row.values.kind,
+                    row.values.roles
                   )
                 }
               >
@@ -408,10 +426,15 @@ const InvitePage: React.FunctionComponent<Props> = ({}) => {
         </InputRowWrapper>
         <Helper>Specify a role for this user.</Helper>
         <RoleSelectorWrapper>
-          <RadioSelector
+          {/* <RadioSelector
             selected={role}
             setSelected={setRole}
             options={roleList}
+          /> */}
+          <RoleSelector
+            options={roles}
+            onChange={setSelectedRoles}
+            values={selectedRoles}
           />
         </RoleSelectorWrapper>
         <ButtonWrapper>
@@ -453,6 +476,72 @@ const InvitePage: React.FunctionComponent<Props> = ({}) => {
 
 export default InvitePage;
 
+const RoleSelector = ({
+  options,
+  onChange,
+  values,
+}: {
+  options: Role[];
+  onChange: (roles: Role[]) => void;
+  values: Role[];
+}) => {
+  const filteredOptions = options.filter(
+    (option) => !values.map((val) => val.name).includes(option.name)
+  );
+
+  return (
+    <>
+      <SearchSelector
+        options={filteredOptions}
+        onSelect={(newRole) => onChange([...values, newRole])}
+        filterBy={(role) => role?.name || ""}
+        getOptionLabel={(role) => role.name || ""}
+        label="Select the roles for this user"
+        placeholder="Select a role"
+      />
+      <RoleTagWrapper>
+        {values.map((role) => (
+          <RoleTag>
+            {role.name}
+            <i
+              className="material-icons-outlined"
+              onClick={() => {
+                onChange(values.filter((value) => value.id !== role.id));
+              }}
+            >
+              delete
+            </i>
+          </RoleTag>
+        ))}
+      </RoleTagWrapper>
+    </>
+  );
+};
+
+const RoleTagWrapper = styled.div`
+  margin-top: 15px;
+`;
+
+const RoleTag = styled.div`
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  background: #f2f2f2;
+  border-radius: 4px;
+  padding: 4px 8px;
+  margin-right: 8px;
+  font-size: 14px;
+  font-weight: 500;
+  color: #000000;
+
+  > i {
+    margin-left: 4px;
+    font-size: 14px;
+
+    cursor: pointer;
+  }
+`;
+
 const Flex = styled.div`
   display: flex;
   align-items: center;
@@ -487,7 +576,7 @@ const SettingsButton = styled(DeleteButton)`
   margin-right: -60px;
 `;
 
-const Role = styled.div`
+const RoleName = styled.div`
   text-transform: capitalize;
   margin-right: 50px;
 `;

+ 2 - 0
dashboard/src/shared/api.tsx

@@ -259,6 +259,8 @@ const createGCPIntegration = baseApi<
 const createInvite = baseApi<
   {
     email: string;
+    roles: string[];
+    // legacy field
     kind: string;
   },
   {