Просмотр исходного кода

dashfolders: POC - Use separate component for "Add permission" #10676

Johannes Schill 8 лет назад
Родитель
Сommit
666d29fafa

+ 17 - 2
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<IContainerProps, any> {
   constructor(props) {
     super(props);
+    this.handleAddPermission = this.handleAddPermission.bind(this);
     this.loadStore();
   }
 
@@ -21,6 +24,11 @@ export class FolderPermissions extends Component<IContainerProps, any> {
     });
   }
 
+  handleAddPermission() {
+    const { permissions } = this.props;
+    permissions.toggleAddPermissions();
+  }
+
   render() {
     const { nav, folder, permissions, backendSrv } = this.props;
 
@@ -34,13 +42,20 @@ export class FolderPermissions extends Component<IContainerProps, any> {
       <div>
         <PageHeader model={nav as any} />
         <div className="page-container page-body">
-          <div className="page-sub-heading">
+          <div className="page-action-bar">
             <h2 className="d-inline-block">Folder Permissions</h2>
             <Tooltip className="page-sub-heading-icon" placement="auto" content={PermissionsInfo}>
               <i className="gicon gicon-question gicon--has-hover" />
             </Tooltip>
+            <div className="page-action-bar__spacer" />
+            <button className="btn btn-success pull-right" onClick={this.handleAddPermission}>
+              <i className="fa fa-plus" />
+              Add Permission
+            </button>
           </div>
-
+          {permissions.isAddPermissionsVisible ? (
+            <AddPermissions permissions={permissions} backendSrv={backendSrv} dashboardId={dashboardId} />
+          ) : null}
           <Permissions permissions={permissions} isFolder={true} dashboardId={dashboardId} backendSrv={backendSrv} />
         </div>
       </div>

+ 144 - 0
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<IProps, any> {
+  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 (
+      <div className="gf-form-inline cta-form">
+        <form name="addPermission" className="gf-form-group" onSubmit={this.handleSubmit}>
+          <h6 className="muted">Add Permission For</h6>
+          <div className="gf-form-inline">
+            <div className="gf-form">
+              <div className="gf-form-select-wrapper">
+                <select className="gf-form-input gf-size-auto" value={newItem.type} onChange={this.typeChanged}>
+                  {aclTypes.map((option, idx) => {
+                    return (
+                      <option key={idx} value={option.value}>
+                        {option.text}
+                      </option>
+                    );
+                  })}
+                </select>
+              </div>
+            </div>
+
+            {newItem.type === 'User' ? (
+              <div className="gf-form">
+                <UserPicker backendSrv={backendSrv} handlePicked={this.userPicked} value={newItem.userId} />
+              </div>
+            ) : null}
+
+            {newItem.type === 'Group' ? (
+              <div className="gf-form">
+                <TeamPicker backendSrv={backendSrv} handlePicked={this.teamPicked} value={newItem.teamId} />
+              </div>
+            ) : null}
+
+            <div className="gf-form">
+              <DescriptionPicker
+                optionsWithDesc={permissionOptions}
+                handlePicked={this.permissionPicked}
+                value={newItem.permission}
+                disabled={false}
+                className={'gf-form-input--form-dropdown-right'}
+              />
+            </div>
+
+            <div className="gf-form">
+              <button className="btn btn-success" type="submit" disabled={!newItem.isValid()}>
+                Save
+              </button>
+            </div>
+          </div>
+        </form>
+        {permissions.error ? (
+          <div className="gf-form width-17">
+            <span ng-if="ctrl.error" className="text-error p-l-1">
+              <i className="fa fa-warning" />
+              {permissions.error}
+            </span>
+          </div>
+        ) : null}
+      </div>
+    );
+  }
+}
+
+export default AddPermissions;

+ 1 - 60
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<IProps, any> {
     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<IProps, any> {
     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 (
       <div className="gf-form-group">
@@ -99,50 +84,6 @@ class Permissions extends Component<IProps, any> {
           fetching={permissions.fetching}
           folderInfo={folderInfo}
         />
-        <div className="gf-form-inline">
-          <form name="addPermission" className="gf-form-group">
-            <h6 className="muted">Add Permission For</h6>
-            <div className="gf-form-inline">
-              <div className="gf-form">
-                <div className="gf-form-select-wrapper">
-                  <select
-                    className="gf-form-input gf-size-auto"
-                    value={permissions.newType}
-                    onChange={this.typeChanged}
-                  >
-                    {aclTypes.map((option, idx) => {
-                      return (
-                        <option key={idx} value={option.value}>
-                          {option.text}
-                        </option>
-                      );
-                    })}
-                  </select>
-                </div>
-              </div>
-
-              {permissions.newType === 'User' ? (
-                <div className="gf-form">
-                  <UserPicker backendSrv={backendSrv} handlePicked={this.userPicked} />
-                </div>
-              ) : null}
-
-              {permissions.newType === 'Group' ? (
-                <div className="gf-form">
-                  <TeamPicker backendSrv={backendSrv} handlePicked={this.teamPicked} />
-                </div>
-              ) : null}
-            </div>
-          </form>
-          {permissions.error ? (
-            <div className="gf-form width-17">
-              <span ng-if="ctrl.error" className="text-error p-l-1">
-                <i className="fa fa-warning" />
-                {permissions.error}
-              </span>
-            </div>
-          ) : null}
-        </div>
       </div>
     );
   }

+ 3 - 1
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<IProps, any> {
 
   render() {
     const AsyncComponent = this.state.creatable ? Select.AsyncCreatable : Select.Async;
-    const { isLoading, handlePicked } = this.props;
+    const { isLoading, handlePicked, value } = this.props;
 
     return (
       <div className="user-picker">
@@ -70,6 +71,7 @@ class TeamPicker extends Component<IProps, any> {
           className="width-8 gf-form-input gf-form-input--form-dropdown"
           optionComponent={PickerOption}
           placeholder="Choose"
+          value={value}
         />
       </div>
     );

+ 5 - 2
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<IProps, any> {
 
   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 (
       <div className="user-picker">
         <AsyncComponent
@@ -70,6 +71,8 @@ class UserPicker extends Component<IProps, any> {
           className="width-8 gf-form-input gf-form-input--form-dropdown"
           optionComponent={PickerOption}
           placeholder="Choose"
+          value={value}
+          autosize={true}
         />
       </div>
     );

+ 1 - 0
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) {

+ 138 - 46
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;

+ 6 - 0
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;
+}