From 834f6d0b7569584a22a015cea9a1da1586525be6 Mon Sep 17 00:00:00 2001 From: Palak Bhojani Date: Thu, 7 Mar 2019 13:46:39 -0800 Subject: [PATCH] Add the ability to remove a member from org --- CHANGELOG.md | 3 +- ui/package-lock.json | 47 +++---- ui/package.json | 2 +- .../components/AddMembersOverlay.tsx | 6 +- .../organizations/components/MemberList.tsx | 18 +-- ui/src/organizations/components/MemberRow.tsx | 42 +++++++ ui/src/organizations/components/Members.tsx | 34 ++++- .../__snapshots__/Members.test.tsx.snap | 117 +++++++----------- ui/src/shared/copy/v2/notifications.ts | 14 ++- 9 files changed, 153 insertions(+), 130 deletions(-) create mode 100644 ui/src/organizations/components/MemberRow.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b3f6fbbe08..00d31c02492 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ On most `linux` systems: ```sh -# Replace with your actual username. +# Replace with your actual username. $ rm -r /home//.influxdbv2/engine ``` @@ -32,6 +32,7 @@ Once completed, `v2.0.0-alpha.5` can be started. 1. [12111](https://github.com/influxdata/influxdb/pull/12111): Add ability to filter resources by clicking a label 1. [12401](https://github.com/influxdata/influxdb/pull/12401): Add ability to add a member to org 1. [12391](https://github.com/influxdata/influxdb/pull/12391): Improve representation of TSM tagsets on disk +1. [12437](https://github.com/influxdata/influxdb/pull/12437): Add ability to remove a member from org ### Bug Fixes diff --git a/ui/package-lock.json b/ui/package-lock.json index bafb57435a2..9b0ab12a82d 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -985,9 +985,9 @@ } }, "@influxdata/influx": { - "version": "0.2.28", - "resolved": "https://registry.npmjs.org/@influxdata/influx/-/influx-0.2.28.tgz", - "integrity": "sha512-jnn6MDbj3/VRh188nYQCGYk4uN4WSyeeFhorPgN8tD6ziCTu000ZI2hgUvsjPL7Um3WXH1yIH7qIfBeP0whFtw==", + "version": "0.2.29", + "resolved": "https://registry.npmjs.org/@influxdata/influx/-/influx-0.2.29.tgz", + "integrity": "sha512-FWajBVLlx82po2NGhDtzEEl1OnuAjtYAgfene/dNPK7wzdov3mGvf1mjzip+lBpb2QZYN+QMbwjqAYfPOg4fdA==", "requires": { "axios": "^0.18.0" } @@ -6085,8 +6085,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -6110,15 +6109,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -6135,22 +6132,19 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -6281,8 +6275,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -6296,7 +6289,6 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -6313,7 +6305,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -6322,15 +6313,13 @@ "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.2.4.tgz", "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==", "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -6351,7 +6340,6 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -6440,8 +6428,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -6455,7 +6442,6 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -6551,8 +6537,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -6594,7 +6579,6 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6616,7 +6600,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6665,15 +6648,13 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=", - "dev": true, - "optional": true + "dev": true } } }, diff --git a/ui/package.json b/ui/package.json index 9488a6b4ef9..e0c4383628a 100644 --- a/ui/package.json +++ b/ui/package.json @@ -137,7 +137,7 @@ }, "dependencies": { "@influxdata/clockface": "0.0.5", - "@influxdata/influx": "0.2.28", + "@influxdata/influx": "0.2.29", "@influxdata/react-custom-scrollbars": "4.3.8", "axios": "^0.18.0", "babel-polyfill": "^6.26.0", diff --git a/ui/src/organizations/components/AddMembersOverlay.tsx b/ui/src/organizations/components/AddMembersOverlay.tsx index 6984b2f7567..570b753a449 100644 --- a/ui/src/organizations/components/AddMembersOverlay.tsx +++ b/ui/src/organizations/components/AddMembersOverlay.tsx @@ -12,7 +12,7 @@ import {UsersMap} from 'src/organizations/components/Members' interface Props { onCloseModal: () => void users: UsersMap - addUser: (user: AddResourceMemberRequestBody) => void + addMember: (user: AddResourceMemberRequestBody) => void } interface State { @@ -53,12 +53,12 @@ export default class AddMembersOverlay extends PureComponent { } private handleSave = () => { - const {users, addUser} = this.props + const {users, addMember} = this.props const {selectedUserIDs} = this.state selectedUserIDs.forEach(id => { if (users[id]) { - addUser({id: id, name: users[id].name}) + addMember({id: id, name: users[id].name}) } }) } diff --git a/ui/src/organizations/components/MemberList.tsx b/ui/src/organizations/components/MemberList.tsx index d408a7c4f36..db11d4f4c5e 100644 --- a/ui/src/organizations/components/MemberList.tsx +++ b/ui/src/organizations/components/MemberList.tsx @@ -3,6 +3,7 @@ import React, {PureComponent} from 'react' // Components import {IndexList} from 'src/clockface' +import MemberRow from 'src/organizations/components/MemberRow' // Types import {ResourceOwner} from '@influxdata/influx' @@ -10,6 +11,7 @@ import {ResourceOwner} from '@influxdata/influx' interface Props { members: ResourceOwner[] emptyState: JSX.Element + onDelete: (member: ResourceOwner) => void } export default class MemberList extends PureComponent { @@ -17,9 +19,9 @@ export default class MemberList extends PureComponent { return ( - - - + + + {this.rows} @@ -29,12 +31,10 @@ export default class MemberList extends PureComponent { } private get rows(): JSX.Element[] { - return this.props.members.map(member => ( - - {member.name} - {member.role} - - + const {members, onDelete} = this.props + + return members.map(member => ( + )) } } diff --git a/ui/src/organizations/components/MemberRow.tsx b/ui/src/organizations/components/MemberRow.tsx new file mode 100644 index 00000000000..2d1d0a746d5 --- /dev/null +++ b/ui/src/organizations/components/MemberRow.tsx @@ -0,0 +1,42 @@ +// Libraries +import React, {PureComponent} from 'react' + +// Components +import {ResourceOwner} from '@influxdata/influx' +import { + IndexList, + ConfirmationButton, + ComponentSize, + Alignment, +} from 'src/clockface' + +interface Props { + member: ResourceOwner + onDelete: (member: ResourceOwner) => void +} + +export default class MemberRow extends PureComponent { + public render() { + const {member} = this.props + + return ( + + {member.name} + {member.role} + + + + + ) + } + + private handleDelete = () => { + const {member, onDelete} = this.props + onDelete(member) + } +} diff --git a/ui/src/organizations/components/Members.tsx b/ui/src/organizations/components/Members.tsx index 6829a18bf6b..58b49f9a129 100644 --- a/ui/src/organizations/components/Members.tsx +++ b/ui/src/organizations/components/Members.tsx @@ -32,6 +32,8 @@ import {client} from 'src/utils/api' import { memberAddSuccess, memberAddFailed, + memberRemoveSuccess, + memberRemoveFailed, } from 'src/shared/copy/v2/notifications' export interface UsersMap { @@ -87,13 +89,19 @@ export default class Members extends PureComponent { searchKeys={['name']} searchTerm={searchTerm} > - {ms => } + {ms => ( + + )} @@ -133,14 +141,14 @@ export default class Members extends PureComponent { this.setState({users: users}) } - private addUser = async (user: AddResourceMemberRequestBody) => { - const {notify} = this.props + private addMember = async (user: AddResourceMemberRequestBody) => { + const {notify, onChange} = this.props try { await client.organizations.addMember(this.props.orgID, user) this.setState({overlayState: OverlayState.Closed}) - notify(memberAddSuccess()) - this.props.onChange() + onChange() + notify(memberAddSuccess(user.name)) } catch (e) { console.error(e) this.setState({overlayState: OverlayState.Closed}) @@ -149,6 +157,20 @@ export default class Members extends PureComponent { } } + private removeMember = async (member: ResourceOwner) => { + const {orgID, notify, onChange} = this.props + + try { + await client.organizations.removeMember(orgID, member.id) + onChange() + notify(memberRemoveSuccess(member.name)) + } catch (e) { + console.error(e) + const message = _.get(e, 'response.data.message', 'Unknown error') + notify(memberRemoveFailed(message)) + } + } + private get emptyState(): JSX.Element { const {orgName} = this.props const {searchTerm} = this.state diff --git a/ui/src/organizations/components/__snapshots__/Members.test.tsx.snap b/ui/src/organizations/components/__snapshots__/Members.test.tsx.snap index 208eea1ef05..499976c92ce 100644 --- a/ui/src/organizations/components/__snapshots__/Members.test.tsx.snap +++ b/ui/src/organizations/components/__snapshots__/Members.test.tsx.snap @@ -6,98 +6,65 @@ exports[`MemberList rendering renders 1`] = ` } > - - - John - - - owner - - - - + - - Jane - - - owner - - - - + - - Smith - - - owner - - - + member={ + Object { + "id": "3", + "links": Object { + "self": "/api/v2/users/3", + }, + "name": "Smith", + "role": "owner", + "status": "active", + } + } + /> `; diff --git a/ui/src/shared/copy/v2/notifications.ts b/ui/src/shared/copy/v2/notifications.ts index 558d0bc8105..13b168d935f 100644 --- a/ui/src/shared/copy/v2/notifications.ts +++ b/ui/src/shared/copy/v2/notifications.ts @@ -213,12 +213,22 @@ export const telegrafDeleteFailed = (telegrafName: string): Notification => ({ message: `Failed to delete telegraf: "${telegrafName}"`, }) -export const memberAddSuccess = (): Notification => ({ +export const memberAddSuccess = (username: string): Notification => ({ ...defaultSuccessNotification, - message: 'Members added successfully', + message: `Member "${username}" was added successfully`, }) export const memberAddFailed = (message: string): Notification => ({ ...defaultErrorNotification, message: `Failed to add members: "${message}"`, }) + +export const memberRemoveSuccess = (memberName: string): Notification => ({ + ...defaultSuccessNotification, + message: `Member "${memberName}" was removed successfully`, +}) + +export const memberRemoveFailed = (message: string): Notification => ({ + ...defaultErrorNotification, + message: `Failed to remove members: "${message}"`, +})