Browse Source

dashfolder: wip: More wip on acl.html2permissions.tsx #10275

Johannes Schill 8 năm trước cách đây
mục cha
commit
c8193e10b9

+ 3 - 2
package.json

@@ -91,7 +91,7 @@
     "typescript": "^2.6.2",
     "webpack": "^3.10.0",
     "webpack-bundle-analyzer": "^2.9.0",
-    "webpack-cleanup-plugin": "^0.5.1",    
+    "webpack-cleanup-plugin": "^0.5.1",
     "webpack-merge": "^4.1.0",
     "zone.js": "^0.7.2"
   },
@@ -140,6 +140,7 @@
     "lodash": "^4.17.4",
     "mobx": "^3.4.1",
     "mobx-react": "^4.3.5",
+    "mobx-react-devtools": "^4.2.15",
     "mobx-state-tree": "^1.3.1",
     "moment": "^2.18.1",
     "mousetrap": "^1.6.0",
@@ -148,8 +149,8 @@
     "react": "^16.2.0",
     "react-dom": "^16.2.0",
     "react-grid-layout": "^0.16.2",
-    "react-popper": "^0.7.5",
     "react-highlight-words": "^0.10.0",
+    "react-popper": "^0.7.5",
     "react-select": "^1.1.0",
     "react-sizeme": "^2.3.6",
     "remarkable": "^1.7.1",

+ 115 - 55
public/app/core/components/Permissions/Permissions.tsx

@@ -1,6 +1,10 @@
 import React, { Component } from 'react';
 import PermissionsList from './PermissionsList';
 import _ from 'lodash';
+import DevTools from 'mobx-react-devtools';
+import { inject, observer } from 'mobx-react';
+import { Provider } from 'mobx-react';
+import { store } from 'app/stores/store';
 
 export interface DashboardAcl {
   id?: number;
@@ -24,12 +28,25 @@ export interface IProps {
   error: any;
   newType: any;
   aclTypes: any;
-  typeChanged: any;
   backendSrv: any;
   dashboardId: number;
+  permissions: any;
 }
 
 class Permissions extends Component<IProps, any> {
+  render() {
+    return (
+      <Provider {...store}>
+        <PermissionsInner {...this.props} />
+      </Provider>
+    );
+  }
+}
+
+@inject('permissions')
+@observer
+class PermissionsInner extends Component<IProps, any> {
+  // TODO Remove Inner from Name when we get access via ReactContainer
   dashboardId: any;
   meta: any;
   items: DashboardAcl[];
@@ -50,58 +67,34 @@ class Permissions extends Component<IProps, any> {
 
   constructor(props) {
     super(props);
-    this.dashboardId = this.props.dashboardId;
-    this.backendSrv = this.props.backendSrv;
+
+    const { dashboardId, backendSrv, permissions } = this.props;
+
+    this.dashboardId = dashboardId;
+    this.backendSrv = backendSrv;
     this.permissionChanged = this.permissionChanged.bind(this);
-    console.log('this.setState', this.setState);
+    this.typeChanged = this.typeChanged.bind(this);
+    this.removeItem = this.removeItem.bind(this);
+    permissions.load(this.dashboardId);
 
     this.state = {
-      items: [],
-      newType: '',
+      newType: 'Group',
       canUpdate: false,
       error: '',
     };
   }
 
-  componentWillMount() {
-    this.getAcl(this.props.dashboardId);
-  }
-
-  getAcl(dashboardId: number) {
-    return this.backendSrv.get(`/api/dashboards/id/${dashboardId}/acl`).then(result => {
-      console.log('this', this.setState);
-      const items = result.map(this.prepareViewModel.bind(this));
-      // this.items = _.map(result, this.prepareViewModel.bind(this));
-      this.setState(prevState => {
-        return {
-          ...prevState,
-          items: this.sortItems(items),
-        };
-      });
-    });
-  }
-
   sortItems(items) {
     return _.orderBy(items, ['sortRank', 'sortName'], ['desc', 'asc']);
   }
 
-  permissionChanged() {
-    this.setState(prevState => {
-      return {
-        ...prevState,
-        canUpdate: true,
-      };
-    });
+  permissionChanged(evt) {
+    // TODO
   }
 
   removeItem(index) {
-    this.setState(prevState => {
-      return {
-        ...prevState,
-        items: this.state.items.splice(index, 1),
-        canUpdate: true,
-      };
-    });
+    const { permissions } = this.props;
+    permissions.removeStoreItem(index);
   }
 
   update() {
@@ -168,29 +161,91 @@ class Permissions extends Component<IProps, any> {
     return item;
   }
 
-  //   componentWillUpdate(nextProps, nextState) {
-  //     console.log('nextProps', nextProps);
-  //     console.log('nextState', nextState);
-  //   }
+  isDuplicate(origItem, newItem) {
+    if (origItem.inherited) {
+      return false;
+    }
 
-  //   componentWillReceiveProps(nextProps) {
-  //     console.log('nextPropzzzz', nextProps);
-  //   }
+    return (
+      (origItem.role && newItem.role && origItem.role === newItem.role) ||
+      (origItem.userId && newItem.userId && origItem.userId === newItem.userId) ||
+      (origItem.teamId && newItem.teamId && origItem.teamId === newItem.teamId)
+    );
+  }
 
-  render() {
-    const { error, newType, aclTypes, typeChanged } = this.props;
+  isValid(item) {
+    const dupe = _.find(this.items, it => {
+      return this.isDuplicate(it, item);
+    });
+
+    if (dupe) {
+      this.error = this.duplicateError;
+      return false;
+    }
 
-    const { items, canUpdate } = this.state;
+    return true;
+  }
 
-    const handleTypeChange = () => {
-      typeChanged();
-    };
+  addNewItem(item) {
+    if (!this.isValid(item)) {
+      return;
+    }
+    this.error = '';
+
+    item.dashboardId = this.dashboardId;
+
+    let newItems = this.state.items;
+    newItems.push(this.prepareViewModel(item));
+
+    this.setState(prevState => {
+      return {
+        ...prevState,
+        items: this.sortItems(newItems),
+        canUpdate: true,
+      };
+    });
+  }
+
+  resetNewType() {
+    this.setState(prevState => {
+      return {
+        newType: 'Group',
+      };
+    });
+  }
+
+  typeChanged(evt) {
+    const { value } = evt.target;
+    this.setState(prevState => {
+      return {
+        ...prevState,
+        newType: value,
+      };
+    });
+  }
+
+  typeChanged___() {
+    const { newType } = this.state;
+    if (newType === 'Viewer' || newType === 'Editor') {
+      this.addNewItem({ permission: 1, role: newType });
+      this.resetNewType();
+      this.setState(prevState => {
+        return {
+          ...prevState,
+          canUpdate: true,
+        };
+      });
+    }
+  }
+
+  render() {
+    const { error, aclTypes, permissions } = this.props;
+    const { newType } = this.state;
 
     return (
       <div className="gf-form-group">
-        asd
         <PermissionsList
-          permissions={items}
+          permissions={permissions.items.toJS()}
           permissionsOptions={this.permissionOptions}
           removeItem={this.removeItem}
           permissionChanged={this.permissionChanged}
@@ -202,7 +257,7 @@ class Permissions extends Component<IProps, any> {
             <div className="gf-form-inline">
               <div className="gf-form">
                 <div className="gf-form-select-wrapper">
-                  <select className="gf-form-input gf-size-auto" onChange={handleTypeChange}>
+                  <select className="gf-form-input gf-size-auto" onChange={this.typeChanged}>
                     {aclTypes.map((option, idx) => {
                       return (
                         <option key={idx} value={option.value}>
@@ -222,12 +277,16 @@ class Permissions extends Component<IProps, any> {
               </div>
               {newType === 'User' ? (
                 <div className="gf-form">
+                  {' '}
+                  User picker
                   <user-picker user-picked="ctrl.userPicked($user)" />
                 </div>
               ) : null}
 
               {newType === 'Group' ? (
                 <div className="gf-form">
+                  {' '}
+                  Team picker
                   <team-picker team-picked="ctrl.groupPicked($group)" />
                 </div>
               ) : null}
@@ -243,11 +302,12 @@ class Permissions extends Component<IProps, any> {
           ) : null}
         </div>
         <div className="gf-form-button-row">
-          <button type="button" className="btn btn-danger" onClick={this.update} disabled={!canUpdate}>
+          <button type="button" className="btn btn-danger" onClick={this.update} disabled={!permissions.canUpdate}>
             Update Permissions
           </button>
         </div>
         asd3
+        <DevTools />
       </div>
     );
   }

+ 0 - 10
public/app/core/components/Permissions/PermissionsList.tsx

@@ -9,18 +9,8 @@ export interface IProps {
 }
 
 class PermissionsList extends Component<IProps, any> {
-  // componentWillUpdate(nextProps, nextState) {
-  //     console.log('nextProps', nextProps);
-  //     console.log('nextState', nextState);
-  // }
-
-  // componentWillReceiveProps(nextProps) {
-  //     console.log('nextPropzzzz', nextProps);
-  // }
-
   render() {
     const { permissions, permissionsOptions, removeItem, permissionChanged } = this.props;
-
     return (
       <table className="filter-table gf-form-group">
         <tbody>

+ 1 - 1
public/app/core/components/Permissions/PermissionsListItem.tsx

@@ -12,7 +12,7 @@ export default ({ item, permissionsOptions, removeItem, permissionChanged, itemI
 
   const handleChangePermission = evt => {
     evt.preventDefault();
-    permissionChanged();
+    permissionChanged(itemIndex, evt.target.value);
   };
 
   return (

+ 0 - 15
public/app/features/dashboard/acl/acl.html

@@ -6,21 +6,6 @@
   dashboardId="ctrl.dashboard.id"
   backendSrv="ctrl.backendSrv" />
 
-
-<per m i s sions
-  permissions="{{ctrl.dummyItems}}"
-  permissionsOptions="{{ctrl.permissionOptions}}"
-  removeItem="ctrl.removeItem"
-  permissionChanged="ctrl.permissionChanged"
-  error="{{ctrl.error}}"
-  newType="ctrl.newType"
-  aclTypes="{{ctrl.aclTypes}}"
-  typeChanged="ctrl.typeChanged"
-  dashboardId="ctrl.dashboard.id"
-  backendSrv="ctrl.backendSrv"/>
-
-
-
 <div class="gf-form-group">
   <table class="filter-table gf-form-group">
     <tr ng-repeat="acl in ctrl.items" ng-class="{'gf-form-disabled': acl.inherited}">

+ 74 - 0
public/app/stores/PermissionsStore/PermissionsStore.ts

@@ -0,0 +1,74 @@
+import { types, getEnv, flow } from 'mobx-state-tree';
+import { PermissionsStoreItem } from './PermissionsStoreItem';
+
+export const PermissionsStore = types
+  .model('PermissionsStore', {
+    fetching: types.boolean,
+    canUpdate: types.boolean,
+    items: types.optional(types.array(PermissionsStoreItem), []),
+    originalItems: types.optional(types.array(PermissionsStoreItem), []),
+  })
+  //   .views(self => ({
+  //     canUpdate: () => {
+  //         const itemsSnapshot = getSnapshot(self.items);
+  //         const originalItemsSnapshot = getSnapshot(self.originalItems);
+  //         console.log('itemsSnapshot', itemsSnapshot);
+  //         console.log('editItemsSnapshot', originalItemsSnapshot);
+  //         return true;
+  //     }
+  //   }))
+  .actions(self => ({
+    load: flow(function* load(dashboardId: number) {
+      self.fetching = true;
+      const backendSrv = getEnv(self).backendSrv;
+      const res = yield backendSrv.get(`/api/dashboards/id/${dashboardId}/acl`);
+      const items = prepareServerResponse(res, dashboardId);
+      self.items = items;
+      self.originalItems = items;
+      self.fetching = false;
+    }),
+    addStoreItem: () => {
+      self.canUpdate = true;
+    },
+    removeStoreItem: idx => {
+      self.items.splice(idx, 1);
+      self.canUpdate = true;
+    },
+  }));
+
+const prepareServerResponse = (response, dashboardId: number) => {
+  return response.map(item => {
+    // TODO: this.meta
+    // item.inherited = !this.meta.isFolder && this.dashboardId !== item.dashboardId;
+    item.inherited = dashboardId !== item.dashboardId;
+    item.sortRank = 0;
+    if (item.userId > 0) {
+      item.icon = 'fa fa-fw fa-user';
+      //   item.nameHtml = this.$sce.trustAsHtml(item.userLogin);
+      item.nameHtml = item.userLogin;
+      item.sortName = item.userLogin;
+      item.sortRank = 10;
+    } else if (item.teamId > 0) {
+      item.icon = 'fa fa-fw fa-users';
+      //   item.nameHtml = this.$sce.trustAsHtml(item.team);
+      item.nameHtml = item.team;
+      item.sortName = item.team;
+      item.sortRank = 20;
+    } else if (item.role) {
+      item.icon = 'fa fa-fw fa-street-view';
+      //   item.nameHtml = this.$sce.trustAsHtml(`Everyone with <span class="query-keyword">${item.role}</span> Role`);
+      item.nameHtml = `Everyone with <span class="query-keyword">${item.role}</span> Role`;
+      item.sortName = item.role;
+      item.sortRank = 30;
+      if (item.role === 'Viewer') {
+        item.sortRank += 1;
+      }
+    }
+
+    if (item.inherited) {
+      item.sortRank += 100;
+    }
+
+    return item;
+  });
+};

+ 25 - 0
public/app/stores/PermissionsStore/PermissionsStoreItem.ts

@@ -0,0 +1,25 @@
+import { types } from 'mobx-state-tree';
+
+export const PermissionsStoreItem = types
+  .model('PermissionsStoreItem', {
+    dashboardId: types.number,
+    id: types.number,
+    permission: types.number,
+    permissionName: types.string,
+    role: types.maybe(types.string),
+    team: types.string,
+    teamId: types.number,
+    userEmail: types.string,
+    userId: types.number,
+    userLogin: types.string,
+    inherited: types.maybe(types.boolean),
+    sortRank: types.maybe(types.number),
+    icon: types.maybe(types.string),
+    nameHtml: types.maybe(types.string),
+    sortName: types.maybe(types.string),
+  })
+  .actions(self => ({
+    updateRole: role => {
+      self.role = role;
+    },
+  }));

+ 6 - 0
public/app/stores/RootStore/RootStore.ts

@@ -5,6 +5,7 @@ import { NavStore } from './../NavStore/NavStore';
 import { AlertListStore } from './../AlertListStore/AlertListStore';
 import { ViewStore } from './../ViewStore/ViewStore';
 import { FolderStore } from './../FolderStore/FolderStore';
+import { PermissionsStore } from './../PermissionsStore/PermissionsStore';
 
 export const RootStore = types.model({
   search: types.optional(SearchStore, {
@@ -17,6 +18,11 @@ export const RootStore = types.model({
   alertList: types.optional(AlertListStore, {
     rules: [],
   }),
+  permissions: types.optional(PermissionsStore, {
+    fetching: false,
+    canUpdate: false,
+    items: [],
+  }),
   view: types.optional(ViewStore, {
     path: '',
     query: {},

+ 4 - 0
yarn.lock

@@ -6467,6 +6467,10 @@ mkdirp@0.5.1, mkdirp@0.x.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdi
   dependencies:
     minimist "0.0.8"
 
+mobx-react-devtools@^4.2.15:
+  version "4.2.15"
+  resolved "https://registry.yarnpkg.com/mobx-react-devtools/-/mobx-react-devtools-4.2.15.tgz#881c038fb83db4dffd1e72bbaf5374d26b2fdebb"
+
 mobx-react@^4.3.5:
   version "4.3.5"
   resolved "https://registry.yarnpkg.com/mobx-react/-/mobx-react-4.3.5.tgz#76853f2f2ef4a6f960c374bcd9f01e875929c04c"