Skip to content
This repository has been archived by the owner on May 31, 2023. It is now read-only.

Add Role management #72

Merged
merged 2 commits into from
Sep 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"@types/reach__router": "^1.3.7",
"@types/react": "^16.9.0",
"@types/react-dom": "^16.9.0",
"msw": "~0.32.0",
"typescript": "^3.7.2"
},
"scripts": {
Expand Down
33 changes: 29 additions & 4 deletions packages/app/src/api/projects/generated/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ export interface RoleProjectsAPI {
* @type {string}
* @memberof RoleProjectsAPI
*/
name?: string;
displayName?: string;
/**
* The description for the role.
* @type {string}
Expand All @@ -496,6 +496,19 @@ export interface RoleProjectsAPI {
*/
permissions?: Array<string>;
}
/**
*
* @export
* @interface RoleResponseProjectsAPI
*/
export interface RoleResponseProjectsAPI {
/**
*
* @type {RoleProjectsAPI}
* @memberof RoleResponseProjectsAPI
*/
role?: RoleProjectsAPI;
}
/**
*
* @export
Expand Down Expand Up @@ -532,7 +545,13 @@ export interface RolesProjectsAPI {
* @type {Array<RoleProjectsAPI>}
* @memberof RolesProjectsAPI
*/
value?: Array<RoleProjectsAPI>;
roles?: Array<RoleProjectsAPI>;
/**
*
* @type {LinksProjectsAPI}
* @memberof RolesProjectsAPI
*/
_links?: LinksProjectsAPI;
}
/**
*
Expand Down Expand Up @@ -2018,7 +2037,10 @@ export const ProjectRolesApiFp = function(configuration?: Configuration) {
body?: RoleCreateProjectsAPI,
Accept?: string,
options?: any
): (fetch?: FetchAPI, basePath?: string) => Promise<RoleProjectsAPI> {
): (
fetch?: FetchAPI,
basePath?: string
) => Promise<RoleResponseProjectsAPI> {
const localVarFetchArgs = ProjectRolesApiFetchParamCreator(
configuration
).createProjectRole(id, Authorization, body, Accept, options);
Expand Down Expand Up @@ -2126,7 +2148,10 @@ export const ProjectRolesApiFp = function(configuration?: Configuration) {
body?: RoleUpdateProjectsAPI,
Accept?: string,
options?: any
): (fetch?: FetchAPI, basePath?: string) => Promise<RoleProjectsAPI> {
): (
fetch?: FetchAPI,
basePath?: string
) => Promise<RoleResponseProjectsAPI> {
const localVarFetchArgs = ProjectRolesApiFetchParamCreator(
configuration
).updateProjectRole(id, roleId, Authorization, body, Accept, options);
Expand Down
19 changes: 15 additions & 4 deletions packages/app/src/api/projects/openapi/projectsAdjusted.json
Original file line number Diff line number Diff line change
Expand Up @@ -793,7 +793,7 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Role"
"$ref": "#/components/schemas/role-response"
},
"example": {
"role": {
Expand Down Expand Up @@ -1948,7 +1948,7 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Role"
"$ref": "#/components/schemas/role-response"
},
"example": {
"role": {
Expand Down Expand Up @@ -2849,14 +2849,22 @@
}
}
},
"role-response": {
"type": "object",
"properties": {
"role": {
"$ref": "#/components/schemas/Role"
}
}
},
"Role": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "The role id."
},
"name": {
"displayName": {
"type": "string",
"description": "The display name for the role."
},
Expand All @@ -2876,12 +2884,15 @@
"Roles": {
"type": "object",
"properties": {
"value": {
"roles": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Role"
},
"description": "List of roles."
},
"_links": {
"$ref": "#/components/schemas/Links"
}
}
},
Expand Down
83 changes: 83 additions & 0 deletions packages/app/src/api/projects/projectsClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
ProjectRolesApi,
ProjectsApi,
ProjectUsersApi,
RoleCreateProjectsAPI,
RoleUpdateProjectsAPI,
TeamMemberAddByNamesProjectsAPI,
} from "./generated";

Expand All @@ -31,6 +33,66 @@ export class ProjectsClient {
this.projectsApi = new ProjectsApi(undefined, baseUrl);
}

/**
* See {@link ProjectRolesApi.createProjectRole} for details.
* @param projectId
* @param role
* @returns
*/
async createProjectRole(projectId: string, role: RoleCreateProjectsAPI) {
return this.rolesApi.createProjectRole(
projectId,
this.accessToken,
role,
ACCEPT
);
}

/**
* See {@link ProjectRolesApi.updateProjectRole} for details.
* @param projectId
* @param roleId
* @param role
* @returns
*/
async updateProjectRole(
projectId: string,
roleId: string,
role: RoleUpdateProjectsAPI
) {
return this.rolesApi.updateProjectRole(
projectId,
roleId,
this.accessToken,
role,
ACCEPT
);
}

/**
* See {@link ProjectRolesApi.getProjectRoles} for details.
* @param projectId
* @returns
*/
async getProjectRoles(projectId: string) {
return this.rolesApi.getProjectRoles(projectId, this.accessToken, ACCEPT);
}

/**
* See {@link ProjectRolesApi.deleteProjectRole} for details.
* @param projectId
* @param roleId
* @returns
*/
async deleteProjectRole(projectId: string, roleId: string) {
return this.rolesApi.deleteProjectRole(
projectId,
roleId,
this.accessToken,
ACCEPT
);
}

/**
* See {@link ProjectUsersApi.getProjectTeamMembers} for details.
* @param projectId
Expand Down Expand Up @@ -81,6 +143,27 @@ export class ProjectsClient {
);
}

/**
* See {@link ProjectUsersApi.updateProjectTeamMemberRoles} for details
* @param projectId
* @param memberId
* @param roleIds
* @returns
*/
async updateProjectMemberRoles(
projectId: string,
memberId: string,
roleIds: string[]
) {
return this.usersApi.updateProjectTeamMemberRoles(
projectId,
memberId,
this.accessToken,
{ roleIds },
ACCEPT
);
}

/**
*
* @param errorResponse
Expand Down
58 changes: 52 additions & 6 deletions packages/app/src/routers/MembersRouter/Members.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@
*
* This code is for demonstration purposes and should not be considered production ready.
*--------------------------------------------------------------------------------------------*/
import { Table, Title } from "@itwin/itwinui-react";
import { SvgEdit } from "@itwin/itwinui-icons-react";
import { IconButton, Table, Title } from "@itwin/itwinui-react";
import { RouteComponentProps } from "@reach/router";
import React from "react";

import { TeamMemberProjectsAPI } from "../../api/projects/generated";
import {
RoleProjectsAPI,
TeamMemberProjectsAPI,
} from "../../api/projects/generated";
import { ProjectsClient } from "../../api/projects/projectsClient";
import { useApiPrefix } from "../../api/useApiPrefix";
import { CreateTypeFromInterface } from "../../utils";
Expand All @@ -17,6 +21,7 @@ import {
skeletonRows,
} from "../SynchronizationRouter/components/SkeletonCell";
import { AddMemberInput } from "./components/AddMemberInput";
import { EditMemberRoleCell } from "./components/EditMemberRoleCell";
import { RemoveMemberCell } from "./components/RemoveMemberCell";
import "./Members.scss";

Expand All @@ -25,7 +30,7 @@ interface MembersProps extends RouteComponentProps {
projectId?: string;
}
type TeamMember = CreateTypeFromInterface<TeamMemberProjectsAPI>;
export const Members = ({ accessToken, projectId }: MembersProps) => {
export const Members = ({ accessToken, projectId, navigate }: MembersProps) => {
const urlPrefix = useApiPrefix();

const [users, setUsers] = React.useState<TeamMember[]>(skeletonRows);
Expand All @@ -39,11 +44,28 @@ export const Members = ({ accessToken, projectId }: MembersProps) => {
const { members } = await client.getProjectUsers(projectId);
setUsers(members ?? []);
} catch (error) {
setError(await client.extractAPIErrorMessage(error));
const errorResponse = error as Response;
setError(await client.extractAPIErrorMessage(errorResponse));
}
}, [accessToken, projectId, urlPrefix]);
React.useEffect(() => void fetchUsers(), [fetchUsers]);

const [roles, setRoles] = React.useState<RoleProjectsAPI[]>(skeletonRows);
const fetchRoles = React.useCallback(async () => {
if (!projectId) {
return;
}
const client = new ProjectsClient(urlPrefix, accessToken);
try {
const { roles } = await client.getProjectRoles(projectId);
setRoles(roles ?? []);
} catch (error) {
const errorResponse = error as Response;
setError(await client.extractAPIErrorMessage(errorResponse));
}
}, [accessToken, projectId, urlPrefix]);
React.useEffect(() => void fetchRoles(), [fetchRoles]);

return (
<div className="idp-scrolling-container members-management">
<div className="idp-content-margins">
Expand Down Expand Up @@ -82,7 +104,31 @@ export const Members = ({ accessToken, projectId }: MembersProps) => {
Cell: SkeletonCell,
Header: "Organization",
},
{ accessor: "roles", Cell: SkeletonCell, Header: "Roles" },
{
accessor: "roles",
Header: (
<div>
Roles{" "}
<IconButton
title={"Manage roles"}
styleType={"borderless"}
onClick={() => navigate?.("roles")}
>
<SvgEdit />
</IconButton>
</div>
),
Cell: (props) => (
<EditMemberRoleCell
{...props}
projectId={projectId ?? ""}
accessToken={accessToken}
onDataUpdated={fetchUsers}
roles={roles}
onError={setError}
/>
),
},
{
id: "actions",
accessor: "userId",
Expand All @@ -102,7 +148,7 @@ export const Members = ({ accessToken, projectId }: MembersProps) => {
],
},
],
[accessToken, fetchUsers, projectId]
[accessToken, fetchUsers, navigate, projectId, roles]
)}
emptyTableContent={
error ||
Expand Down
5 changes: 5 additions & 0 deletions packages/app/src/routers/MembersRouter/MembersRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import React from "react";

import { SelectionRouter } from "../SelectionRouter/SelectionRouter";
import { Members } from "./Members";
import { RolesRouter } from "./RolesRouter";

interface MembersRouterProps extends RouteComponentProps {
accessToken: string;
Expand All @@ -19,6 +20,10 @@ export const MembersRouter = ({ accessToken }: MembersRouterProps) => {
<Router className="full-height-container">
<SelectionRouter accessToken={accessToken} path="*" />
<Members accessToken={accessToken} path="/project/:projectId/*" />
<RolesRouter
accessToken={accessToken}
path="/project/:projectId/roles/*"
/>
</Router>
);
};
33 changes: 33 additions & 0 deletions packages/app/src/routers/MembersRouter/RoleBase.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*
* This code is for demonstration purposes and should not be considered production ready.
*--------------------------------------------------------------------------------------------*/
.idp-scrolling-role-base {
display: flex;
flex-direction: column;
overflow: hidden;
max-height: 100%;

> div {
display: flex;
flex-direction: column;
overflow: hidden;

> .idp-role-base {
overflow: hidden;
padding: 0;

> div:first-child {
display: flex;
flex-direction: column;
overflow: hidden;

> div:nth-child(2) {
overflow: scroll;
}
}
}
}
}
Loading