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

dashfolders: support creating new folder from the folder picker. #10197

Marcus Efraimsson 8 лет назад
Родитель
Сommit
60d7e4308c

+ 1 - 1
public/app/core/services/backend_srv.ts

@@ -251,7 +251,7 @@ export class BackendSrv {
   createDashboardFolder(name) {
   createDashboardFolder(name) {
     const dash = {
     const dash = {
       schemaVersion: 16,
       schemaVersion: 16,
-      title: name,
+      title: name.trim(),
       editable: true,
       editable: true,
       panels: []
       panels: []
     };
     };

+ 29 - 28
public/app/features/dashboard/all.ts

@@ -1,31 +1,32 @@
-import "./dashboard_ctrl";
-import "./alerting_srv";
-import "./history/history";
-import "./dashboardLoaderSrv";
-import "./dashnav/dashnav";
-import "./submenu/submenu";
-import "./save_as_modal";
-import "./save_modal";
-import "./shareModalCtrl";
-import "./shareSnapshotCtrl";
-import "./dashboard_srv";
-import "./view_state_srv";
-import "./time_srv";
-import "./unsavedChangesSrv";
-import "./unsaved_changes_modal";
-import "./timepicker/timepicker";
-import "./upload";
-import "./export/export_modal";
-import "./export_data/export_data_modal";
-import "./ad_hoc_filters";
-import "./repeat_option/repeat_option";
-import "./dashgrid/DashboardGridDirective";
-import "./dashgrid/PanelLoader";
-import "./dashgrid/RowOptions";
-import "./acl/acl";
-import "./folder_picker/picker";
-import "./move_to_folder_modal/move_to_folder";
-import "./settings/settings";
+import './dashboard_ctrl';
+import './alerting_srv';
+import './history/history';
+import './dashboardLoaderSrv';
+import './dashnav/dashnav';
+import './submenu/submenu';
+import './save_as_modal';
+import './save_modal';
+import './shareModalCtrl';
+import './shareSnapshotCtrl';
+import './dashboard_srv';
+import './view_state_srv';
+import './validation_srv';
+import './time_srv';
+import './unsavedChangesSrv';
+import './unsaved_changes_modal';
+import './timepicker/timepicker';
+import './upload';
+import './export/export_modal';
+import './export_data/export_data_modal';
+import './ad_hoc_filters';
+import './repeat_option/repeat_option';
+import './dashgrid/DashboardGridDirective';
+import './dashgrid/PanelLoader';
+import './dashgrid/RowOptions';
+import './acl/acl';
+import './folder_picker/folder_picker';
+import './move_to_folder_modal/move_to_folder';
+import './settings/settings';
 
 
 import coreModule from "app/core/core_module";
 import coreModule from "app/core/core_module";
 import { DashboardListCtrl } from "./dashboard_list_ctrl";
 import { DashboardListCtrl } from "./dashboard_list_ctrl";

+ 46 - 0
public/app/features/dashboard/folder_picker/folder_picker.html

@@ -0,0 +1,46 @@
+<div class="gf-form-inline">
+  <div class="gf-form">
+    <label class="gf-form-label {{ctrl.labelClass}}">Folder</label>
+    <div class="dropdown" ng-hide="ctrl.createNewFolder">
+      <gf-form-dropdown model="ctrl.folder"
+        get-options="ctrl.getOptions($query)"
+        on-change="ctrl.onFolderChange($option)">
+      </gf-form-dropdown>
+    </div>
+    <input type="text"
+      class="gf-form-input max-width-10"
+      ng-show="ctrl.createNewFolder"
+      give-focus="ctrl.createNewFolder"
+      ng-model="ctrl.newFolderName"
+      ng-model-options="{ debounce: 400 }"
+      ng-class="{'validation-error': !ctrl.isNewFolderNameValid()}"
+      ng-change="ctrl.newFolderNameChanged()" />
+  </div>
+  <div class="gf-form" ng-show="ctrl.createNewFolder">
+    <label class="gf-form-label text-success"
+      ng-show="ctrl.newFolderNameTouched && !ctrl.hasValidationError">
+      <i class="fa fa-check"></i>
+    </label>
+  </div>
+  <div class="gf-form" ng-show="ctrl.createNewFolder">
+    <button class="gf-form-label"
+      ng-click="ctrl.createFolder($event)"
+      ng-disabled="!ctrl.newFolderNameTouched || ctrl.hasValidationError">
+      <i class="fa fa-fw fa-save"></i>&nbsp;Create
+    </button>
+  </div>
+  <div class="gf-form" ng-show="ctrl.createNewFolder">
+    <button class="gf-form-label"
+      ng-click="ctrl.cancelCreateFolder($event)">
+      Cancel
+  </button>
+  </div>
+</div>
+<div class="gf-form-inline" ng-if="ctrl.newFolderNameTouched && ctrl.hasValidationError">
+  <div class="gf-form gf-form--grow">
+    <label class="gf-form-label text-warning gf-form-label--grow">
+      <i class="fa fa-warning"></i>
+      {{ctrl.validationError}}
+    </label>
+  </div>
+</div>

+ 161 - 0
public/app/features/dashboard/folder_picker/folder_picker.ts

@@ -0,0 +1,161 @@
+///<reference path="../../../headers/common.d.ts" />
+
+import _ from 'lodash';
+import coreModule from 'app/core/core_module';
+import appEvents from 'app/core/app_events';
+
+export class FolderPickerCtrl {
+  initialTitle: string;
+  initialFolderId?: number;
+  labelClass: string;
+  onChange: any;
+  onLoad: any;
+  onCreateFolder: any;
+  enterFolderCreation: any;
+  exitFolderCreation: any;
+  enableCreateNew: boolean;
+  rootName = 'Root';
+  folder: any;
+  createNewFolder: boolean;
+  newFolderName: string;
+  newFolderNameTouched: boolean;
+  hasValidationError: boolean;
+  validationError: any;
+
+  /** @ngInject */
+  constructor(private backendSrv, private validationSrv) {
+    if (!this.labelClass) {
+      this.labelClass = "width-7";
+    }
+
+    this.loadInitialValue();
+  }
+
+  getOptions(query) {
+    var params = {
+      query: query,
+      type: 'dash-folder',
+    };
+
+    return this.backendSrv.search(params).then(result => {
+      if (query === '' ||
+          query.toLowerCase() === "r" ||
+          query.toLowerCase() === "ro" ||
+          query.toLowerCase() === "roo" ||
+          query.toLowerCase() === "root") {
+        result.unshift({title: this.rootName, id: 0});
+      }
+
+      if (this.enableCreateNew && query === '') {
+        result.unshift({title: '-- New Folder --', id: -1});
+      }
+
+      return _.map(result, item => {
+        return {text: item.title, value: item.id};
+      });
+    });
+  }
+
+  onFolderChange(option) {
+    if (option.value === -1) {
+      this.createNewFolder = true;
+      this.enterFolderCreation();
+      return;
+    }
+    this.onChange({$folder: {id: option.value, title: option.text}});
+  }
+
+  newFolderNameChanged() {
+    this.newFolderNameTouched = true;
+
+    this.validationSrv.validateNewDashboardOrFolderName(this.newFolderName)
+      .then(() => {
+        this.hasValidationError = false;
+      })
+      .catch(err => {
+        this.hasValidationError = true;
+        this.validationError = err.message;
+      });
+  }
+
+  createFolder(evt) {
+    if (evt) {
+      evt.stopPropagation();
+      evt.preventDefault();
+    }
+
+    return this.backendSrv.createDashboardFolder(this.newFolderName).then(result => {
+      appEvents.emit('alert-success', ['Folder Created', 'OK']);
+
+      this.closeCreateFolder();
+      this.folder = {text: result.dashboard.title, value: result.dashboard.id};
+      this.onFolderChange(this.folder);
+    });
+  }
+
+  cancelCreateFolder(evt) {
+    if (evt) {
+      evt.stopPropagation();
+      evt.preventDefault();
+    }
+
+    this.closeCreateFolder();
+    this.loadInitialValue();
+  }
+
+  private closeCreateFolder() {
+    this.exitFolderCreation();
+    this.createNewFolder = false;
+    this.hasValidationError = false;
+    this.validationError = null;
+    this.newFolderName = '';
+    this.newFolderNameTouched = false;
+  }
+
+  private loadInitialValue() {
+    if (this.initialFolderId && this.initialFolderId > 0) {
+      this.getOptions('').then(result => {
+        this.folder = _.find(result, {value: this.initialFolderId});
+        this.onFolderLoad();
+      });
+    } else {
+      if (this.initialTitle) {
+        this.folder = {text: this.initialTitle, value: null};
+      } else {
+        this.folder = {text: this.rootName, value: 0};
+      }
+
+      this.onFolderLoad();
+    }
+  }
+
+  private onFolderLoad() {
+    if (this.onLoad) {
+      this.onLoad({$folder: {id: this.folder.value, title: this.folder.text}});
+    }
+  }
+}
+
+export function folderPicker() {
+  return {
+    restrict: 'E',
+    templateUrl: 'public/app/features/dashboard/folder_picker/folder_picker.html',
+    controller: FolderPickerCtrl,
+    bindToController: true,
+    controllerAs: 'ctrl',
+    scope: {
+      initialTitle: '<',
+      initialFolderId: '<',
+      labelClass: '@',
+      rootName: '@',
+      onChange: '&',
+      onLoad: '&',
+      onCreateFolder: '&',
+      enterFolderCreation: '&',
+      exitFolderCreation: '&',
+      enableCreateNew: '@'
+    }
+  };
+}
+
+coreModule.directive('folderPicker', folderPicker);

+ 0 - 103
public/app/features/dashboard/folder_picker/picker.ts

@@ -1,103 +0,0 @@
-///<reference path="../../../headers/common.d.ts" />
-
-import coreModule from "app/core/core_module";
-import _ from "lodash";
-
-export class FolderPickerCtrl {
-  initialTitle: string;
-  initialFolderId?: number;
-  labelClass: string;
-  onChange: any;
-  onLoad: any;
-  rootName = "Root";
-  folder: any;
-
-  /** @ngInject */
-  constructor(private backendSrv) {
-    if (!this.labelClass) {
-      this.labelClass = "width-7";
-    }
-
-    if (this.initialFolderId && this.initialFolderId > 0) {
-      this.getOptions("").then(result => {
-        this.folder = _.find(result, { value: this.initialFolderId });
-        this.onFolderLoad();
-      });
-    } else {
-      if (this.initialTitle) {
-        this.folder = { text: this.initialTitle, value: null };
-      } else {
-        this.folder = { text: this.rootName, value: 0 };
-      }
-
-      this.onFolderLoad();
-    }
-  }
-
-  getOptions(query) {
-    var params = {
-      query: query,
-      type: "dash-folder"
-    };
-
-    return this.backendSrv.search(params).then(result => {
-      if (
-        query === "" ||
-        query.toLowerCase() === "r" ||
-        query.toLowerCase() === "ro" ||
-        query.toLowerCase() === "roo" ||
-        query.toLowerCase() === "root"
-      ) {
-        result.unshift({ title: this.rootName, id: 0 });
-      }
-
-      return _.map(result, item => {
-        return { text: item.title, value: item.id };
-      });
-    });
-  }
-
-  onFolderLoad() {
-    if (this.onLoad) {
-      this.onLoad({
-        $folder: { id: this.folder.value, title: this.folder.text }
-      });
-    }
-  }
-
-  onFolderChange(option) {
-    this.onChange({ $folder: { id: option.value, title: option.text } });
-  }
-}
-
-const template = `
-<div class="gf-form">
-  <label class="gf-form-label {{ctrl.labelClass}}">Folder</label>
-  <div class="dropdown">
-    <gf-form-dropdown model="ctrl.folder"
-      get-options="ctrl.getOptions($query)"
-      on-change="ctrl.onFolderChange($option)">
-    </gf-form-dropdown>
-  </div>
-</div>
-`;
-
-export function folderPicker() {
-  return {
-    restrict: "E",
-    template: template,
-    controller: FolderPickerCtrl,
-    bindToController: true,
-    controllerAs: "ctrl",
-    scope: {
-      initialTitle: "<",
-      initialFolderId: "<",
-      labelClass: "@",
-      rootName: "@",
-      onChange: "&",
-      onLoad: "&"
-    }
-  };
-}
-
-coreModule.directive("folderPicker", folderPicker);

+ 48 - 0
public/app/features/dashboard/validation_srv.ts

@@ -0,0 +1,48 @@
+///<reference path="../../headers/common.d.ts" />
+
+import coreModule from 'app/core/core_module';
+
+export class ValidationSrv {
+  rootName = 'root';
+
+  /** @ngInject */
+  constructor(private $q, private backendSrv) {}
+
+  validateNewDashboardOrFolderName(name) {
+    name = (name || '').trim();
+
+    if (name.length === 0) {
+      return this.$q.reject({
+        type: 'REQUIRED',
+        message: 'Name is required'
+      });
+    }
+
+    if (name.toLowerCase() === this.rootName) {
+      return this.$q.reject({
+        type: 'EXISTING',
+        message: 'A folder or dashboard with the same name already exists'
+      });
+    }
+
+    let deferred = this.$q.defer();
+
+    this.backendSrv.search({ query: name }).then(res => {
+      for (let hit of res) {
+        if (name.toLowerCase() === hit.title.toLowerCase()) {
+          deferred.reject({
+            type: 'EXISTING',
+            message: 'A folder or dashboard with the same name already exists'
+          });
+          break;
+        }
+      }
+
+      deferred.resolve();
+    });
+
+    return deferred.promise;
+  }
+}
+
+coreModule.service('validationSrv', ValidationSrv);

+ 4 - 0
public/sass/components/_gf-form.scss

@@ -109,6 +109,10 @@ $input-border: 1px solid $input-border-color;
   &--error {
   &--error {
     color: $critical;
     color: $critical;
   }
   }
+
+  &:disabled {
+    color: $text-color-weak
+  }
 }
 }
 
 
 .gf-form-label + .gf-form-label {
 .gf-form-label + .gf-form-label {