From 666d29fafae85b0d6f315d2bb8ca88f9fd166cbc Mon Sep 17 00:00:00 2001 From: Johannes Schill Date: Wed, 31 Jan 2018 10:39:38 +0100 Subject: [PATCH] dashfolders: POC - Use separate component for "Add permission" #10676 --- .../ManageDashboards/FolderPermissions.tsx | 19 +- .../components/Permissions/AddPermissions.tsx | 144 ++++++++++++++ .../components/Permissions/Permissions.tsx | 61 +----- .../app/core/components/Picker/TeamPicker.tsx | 4 +- .../app/core/components/Picker/UserPicker.tsx | 7 +- .../app/core/components/Picker/withPicker.tsx | 1 + .../PermissionsStore/PermissionsStore.ts | 184 +++++++++++++----- public/sass/components/_gf-form.scss | 6 + 8 files changed, 315 insertions(+), 111 deletions(-) create mode 100644 public/app/core/components/Permissions/AddPermissions.tsx diff --git a/public/app/containers/ManageDashboards/FolderPermissions.tsx b/public/app/containers/ManageDashboards/FolderPermissions.tsx index 3214382732a..98f63a46cbe 100644 --- a/public/app/containers/ManageDashboards/FolderPermissions.tsx +++ b/public/app/containers/ManageDashboards/FolderPermissions.tsx @@ -6,11 +6,14 @@ import PageHeader from 'app/core/components/PageHeader/PageHeader'; import Permissions from 'app/core/components/Permissions/Permissions'; import Tooltip from 'app/core/components/Tooltip/Tooltip'; import PermissionsInfo from 'app/core/components/Permissions/PermissionsInfo'; +import AddPermissions from 'app/core/components/Permissions/AddPermissions'; + @inject('nav', 'folder', 'view', 'permissions') @observer export class FolderPermissions extends Component { constructor(props) { super(props); + this.handleAddPermission = this.handleAddPermission.bind(this); this.loadStore(); } @@ -21,6 +24,11 @@ export class FolderPermissions extends Component { }); } + handleAddPermission() { + const { permissions } = this.props; + permissions.toggleAddPermissions(); + } + render() { const { nav, folder, permissions, backendSrv } = this.props; @@ -34,13 +42,20 @@ export class FolderPermissions extends Component {
-
+

Folder Permissions

+
+
- + {permissions.isAddPermissionsVisible ? ( + + ) : null}
diff --git a/public/app/core/components/Permissions/AddPermissions.tsx b/public/app/core/components/Permissions/AddPermissions.tsx new file mode 100644 index 00000000000..36b57e448e7 --- /dev/null +++ b/public/app/core/components/Permissions/AddPermissions.tsx @@ -0,0 +1,144 @@ +import React, { Component } from 'react'; +import { observer } from 'mobx-react'; +import { aclTypes } from 'app/stores/PermissionsStore/PermissionsStore'; +import UserPicker, { User } from 'app/core/components/Picker/UserPicker'; +import TeamPicker, { Team } from 'app/core/components/Picker/TeamPicker'; +import DescriptionPicker, { OptionWithDescription } from 'app/core/components/Picker/DescriptionPicker'; +import { permissionOptions } from 'app/stores/PermissionsStore/PermissionsStore'; + +export interface IProps { + permissions: any; + backendSrv: any; + dashboardId: any; +} + +@observer +class AddPermissions extends Component { + constructor(props) { + super(props); + this.userPicked = this.userPicked.bind(this); + this.teamPicked = this.teamPicked.bind(this); + this.permissionPicked = this.permissionPicked.bind(this); + this.typeChanged = this.typeChanged.bind(this); + this.handleSubmit = this.handleSubmit.bind(this); + } + + componentWillMount() { + const { permissions } = this.props; + permissions.resetNewType(); + } + + typeChanged(evt) { + const { value } = evt.target; + const { permissions } = this.props; + + // if (value === 'Viewer' || value === 'Editor') { + // // permissions.addStoreItem({ permission: 1, role: value, dashboardId: dashboardId }, dashboardId); + // // this.resetNewType(); + // return; + // } + + permissions.setNewType(value); + } + + userPicked(user: User) { + const { permissions } = this.props; + if (!user) { + permissions.newItem.setUser(null, null); + return; + } + permissions.newItem.setUser(user.id, user.login); + // return permissions.addStoreItem({ userId: user.id, userLogin: user.login, permission: 1 }); + } + + teamPicked(team: Team) { + const { permissions } = this.props; + if (!team) { + permissions.newItem.setTeam(null, null); + return; + } + permissions.newItem.setTeam(team.id, team.name); + } + + permissionPicked(permission: OptionWithDescription) { + const { permissions } = this.props; + permissions.newItem.setPermission(permission.value); + } + + resetNewType() { + const { permissions } = this.props; + permissions.resetNewType(); + } + + handleSubmit(evt) { + evt.preventDefault(); + const { permissions } = this.props; + permissions.addStoreItem(); + } + + render() { + const { permissions, backendSrv } = this.props; + const newItem = permissions.newItem; + + return ( +
+
+
Add Permission For
+
+
+
+ +
+
+ + {newItem.type === 'User' ? ( +
+ +
+ ) : null} + + {newItem.type === 'Group' ? ( +
+ +
+ ) : null} + +
+ +
+ +
+ +
+
+
+ {permissions.error ? ( +
+ + + {permissions.error} + +
+ ) : null} +
+ ); + } +} + +export default AddPermissions; diff --git a/public/app/core/components/Permissions/Permissions.tsx b/public/app/core/components/Permissions/Permissions.tsx index f5af579094b..1849d7da173 100644 --- a/public/app/core/components/Permissions/Permissions.tsx +++ b/public/app/core/components/Permissions/Permissions.tsx @@ -1,9 +1,6 @@ import React, { Component } from 'react'; import PermissionsList from './PermissionsList'; import { observer } from 'mobx-react'; -import UserPicker, { User } from 'app/core/components/Picker/UserPicker'; -import TeamPicker, { Team } from 'app/core/components/Picker/TeamPicker'; -import { aclTypes } from 'app/stores/PermissionsStore/PermissionsStore'; import { FolderInfo } from './FolderInfo'; export interface DashboardAcl { @@ -40,8 +37,6 @@ class Permissions extends Component { this.permissionChanged = this.permissionChanged.bind(this); this.typeChanged = this.typeChanged.bind(this); this.removeItem = this.removeItem.bind(this); - this.userPicked = this.userPicked.bind(this); - this.teamPicked = this.teamPicked.bind(this); this.loadStore(dashboardId, isFolder); } @@ -77,18 +72,8 @@ class Permissions extends Component { permissions.setNewType(value); } - userPicked(user: User) { - const { permissions } = this.props; - return permissions.addStoreItem({ userId: user.id, userLogin: user.login, permission: 1 }); - } - - teamPicked(team: Team) { - const { permissions } = this.props; - return permissions.addStoreItem({ teamId: team.id, team: team.name, permission: 1 }); - } - render() { - const { permissions, folderInfo, backendSrv } = this.props; + const { permissions, folderInfo } = this.props; return (
@@ -99,50 +84,6 @@ class Permissions extends Component { fetching={permissions.fetching} folderInfo={folderInfo} /> -
-
-
Add Permission For
-
-
-
- -
-
- - {permissions.newType === 'User' ? ( -
- -
- ) : null} - - {permissions.newType === 'Group' ? ( -
- -
- ) : null} -
-
- {permissions.error ? ( -
- - - {permissions.error} - -
- ) : null} -
); } diff --git a/public/app/core/components/Picker/TeamPicker.tsx b/public/app/core/components/Picker/TeamPicker.tsx index 4b5a049c0ff..18f7258c221 100644 --- a/public/app/core/components/Picker/TeamPicker.tsx +++ b/public/app/core/components/Picker/TeamPicker.tsx @@ -9,6 +9,7 @@ export interface IProps { isLoading: boolean; toggleLoading: any; handlePicked: (user) => void; + value?: string; } export interface Team { @@ -54,7 +55,7 @@ class TeamPicker extends Component { render() { const AsyncComponent = this.state.creatable ? Select.AsyncCreatable : Select.Async; - const { isLoading, handlePicked } = this.props; + const { isLoading, handlePicked, value } = this.props; return (
@@ -70,6 +71,7 @@ class TeamPicker extends Component { className="width-8 gf-form-input gf-form-input--form-dropdown" optionComponent={PickerOption} placeholder="Choose" + value={value} />
); diff --git a/public/app/core/components/Picker/UserPicker.tsx b/public/app/core/components/Picker/UserPicker.tsx index 141371813a2..99b0ef258ca 100644 --- a/public/app/core/components/Picker/UserPicker.tsx +++ b/public/app/core/components/Picker/UserPicker.tsx @@ -9,6 +9,7 @@ export interface IProps { isLoading: boolean; toggleLoading: any; handlePicked: (user) => void; + value?: string; } export interface User { @@ -53,8 +54,8 @@ class UserPicker extends Component { render() { const AsyncComponent = this.state.creatable ? Select.AsyncCreatable : Select.Async; - const { isLoading, handlePicked } = this.props; - + const { isLoading, handlePicked, value } = this.props; + console.log('value', value); return (
{ className="width-8 gf-form-input gf-form-input--form-dropdown" optionComponent={PickerOption} placeholder="Choose" + value={value} + autosize={true} />
); diff --git a/public/app/core/components/Picker/withPicker.tsx b/public/app/core/components/Picker/withPicker.tsx index bdfcb02676e..cf3954850b2 100644 --- a/public/app/core/components/Picker/withPicker.tsx +++ b/public/app/core/components/Picker/withPicker.tsx @@ -3,6 +3,7 @@ export interface IProps { backendSrv: any; handlePicked: (data) => void; + value?: string; } export default function withPicker(WrappedComponent) { diff --git a/public/app/stores/PermissionsStore/PermissionsStore.ts b/public/app/stores/PermissionsStore/PermissionsStore.ts index 52e5dcdb339..ae100fd0751 100644 --- a/public/app/stores/PermissionsStore/PermissionsStore.ts +++ b/public/app/stores/PermissionsStore/PermissionsStore.ts @@ -13,15 +13,62 @@ export const permissionOptions = [ }, ]; -export const aclTypes = [ - { value: 'Group', text: 'Team' }, - { value: 'User', text: 'User' }, - { value: 'Viewer', text: 'Everyone With Viewer Role' }, - { value: 'Editor', text: 'Everyone With Editor Role' }, -]; +export const aclTypeValues = { + GROUP: { value: 'Group', text: 'Team' }, + USER: { value: 'User', text: 'User' }, + VIEWER: { value: 'Viewer', text: 'Everyone With Viewer Role' }, + EDITOR: { value: 'Editor', text: 'Everyone With Editor Role' }, +}; + +export const aclTypes = Object.keys(aclTypeValues).map(item => aclTypeValues[item]); const defaultNewType = aclTypes[0].value; +const NewPermissionsItem = types + .model('NewPermissionsItem', { + type: types.optional( + types.enumeration(Object.keys(aclTypeValues).map(item => aclTypeValues[item].value)), + defaultNewType + ), + userId: types.maybe(types.number), + userLogin: types.maybe(types.string), + teamId: types.maybe(types.number), + team: types.maybe(types.string), + permission: types.optional(types.number, 1), + }) + .views(self => ({ + isValid: () => { + switch (self.type) { + case aclTypeValues.GROUP.value: + return self.teamId && self.team; + case aclTypeValues.USER.value: + return self.userId && self.userLogin; + case aclTypeValues.VIEWER.value: + case aclTypeValues.EDITOR.value: + return true; + default: + return false; + } + }, + })) + .actions(self => ({ + setUser(userId: number, userLogin: string) { + self.userId = userId; + self.userLogin = userLogin; + self.teamId = null; + self.team = null; + }, + setTeam(teamId: number, team: string) { + self.userId = null; + self.userLogin = null; + self.teamId = teamId; + self.team = team; + }, + setPermission(permission: number) { + self.permission = permission; + }, + })); + export const PermissionsStore = types .model('PermissionsStore', { fetching: types.boolean, @@ -31,6 +78,8 @@ export const PermissionsStore = types error: types.maybe(types.string), originalItems: types.optional(types.array(PermissionsStoreItem), []), newType: types.optional(types.string, defaultNewType), + newItem: types.maybe(NewPermissionsItem), + isAddPermissionsVisible: types.optional(types.boolean, false), }) .views(self => ({ isValid: item => { @@ -46,48 +95,91 @@ export const PermissionsStore = types return true; }, })) - .actions(self => ({ - load: flow(function* load(dashboardId: number, isFolder: boolean) { - const backendSrv = getEnv(self).backendSrv; - self.fetching = true; - self.isFolder = isFolder; - self.dashboardId = dashboardId; - const res = yield backendSrv.get(`/api/dashboards/id/${dashboardId}/acl`); - const items = prepareServerResponse(res, dashboardId, isFolder); - self.items = items; - self.originalItems = items; - self.fetching = false; - }), - addStoreItem: flow(function* addStoreItem(item) { + .actions(self => { + const resetNewType = () => { self.error = null; - if (!self.isValid(item)) { - return undefined; - } + self.newItem = NewPermissionsItem.create(); + }; - self.items.push(prepareItem(item, self.dashboardId, self.isFolder)); - return updateItems(self); - }), - removeStoreItem: flow(function* removeStoreItem(idx: number) { - self.error = null; - self.items.splice(idx, 1); - return updateItems(self); - }), - updatePermissionOnIndex: flow(function* updatePermissionOnIndex( - idx: number, - permission: number, - permissionName: string - ) { - self.error = null; - self.items[idx].updatePermission(permission, permissionName); - return updateItems(self); - }), - setNewType(newType: string) { - self.newType = newType; - }, - resetNewType() { - self.newType = defaultNewType; - }, - })); + return { + load: flow(function* load(dashboardId: number, isFolder: boolean) { + const backendSrv = getEnv(self).backendSrv; + self.fetching = true; + self.isFolder = isFolder; + self.dashboardId = dashboardId; + const res = yield backendSrv.get(`/api/dashboards/id/${dashboardId}/acl`); + const items = prepareServerResponse(res, dashboardId, isFolder); + self.items = items; + self.originalItems = items; + self.fetching = false; + }), + addStoreItem: flow(function* addStoreItem() { + self.error = null; + let item = { + type: self.newItem.type, + permission: self.newItem.permission, + team: undefined, + teamId: undefined, + userLogin: undefined, + userId: undefined, + role: undefined, + }; + switch (self.newItem.type) { + case aclTypeValues.GROUP.value: + item.team = self.newItem.team; + item.teamId = self.newItem.teamId; + break; + case aclTypeValues.USER.value: + item.userLogin = self.newItem.userLogin; + item.userId = self.newItem.userId; + break; + case aclTypeValues.VIEWER.value: + case aclTypeValues.EDITOR.value: + item.role = self.newItem.type; + break; + default: + throw Error('Unknown type: ' + self.newItem.type); + } + + if (!self.isValid(item)) { + throw Error('New item not valid'); + } + + self.items.push(prepareItem(item, self.dashboardId, self.isFolder)); + resetNewType(); + return updateItems(self); + }), + removeStoreItem: flow(function* removeStoreItem(idx: number) { + self.error = null; + self.items.splice(idx, 1); + return updateItems(self); + }), + updatePermissionOnIndex: flow(function* updatePermissionOnIndex( + idx: number, + permission: number, + permissionName: string + ) { + self.error = null; + self.items[idx].updatePermission(permission, permissionName); + return updateItems(self); + }), + setNewType(newType: string) { + self.newItem = NewPermissionsItem.create({ type: newType }); + }, + resetNewType() { + resetNewType(); + }, + toggleAddPermissions() { + self.isAddPermissionsVisible = !self.isAddPermissionsVisible; + }, + showAddPermissions() { + self.isAddPermissionsVisible = true; + }, + hideAddPermissions() { + self.isAddPermissionsVisible = false; + }, + }; + }); const updateItems = self => { self.error = null; diff --git a/public/sass/components/_gf-form.scss b/public/sass/components/_gf-form.scss index 2113bbae43c..dd6c7c39b83 100644 --- a/public/sass/components/_gf-form.scss +++ b/public/sass/components/_gf-form.scss @@ -392,3 +392,9 @@ select.gf-form-input ~ .gf-form-help-icon { top: 10px; color: $text-muted; } + +.cta-form { + padding: 1rem; + background-color: $dark-2; + margin-bottom: 1rem; +}