Commit 60d7e430 by Marcus Efraimsson

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

parent 908b6c8d
......@@ -251,7 +251,7 @@ export class BackendSrv {
createDashboardFolder(name) {
const dash = {
schemaVersion: 16,
title: name,
title: name.trim(),
editable: true,
panels: []
};
......
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 { DashboardListCtrl } from "./dashboard_list_ctrl";
......
<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>
\ No newline at end of file
///<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);
///<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);
///<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);
......@@ -109,6 +109,10 @@ $input-border: 1px solid $input-border-color;
&--error {
color: $critical;
}
&:disabled {
color: $text-color-weak
}
}
.gf-form-label + .gf-form-label {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment