mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge pull request #10278 from grafana/10197_new_folder
Create new folder from the folder picker component
This commit is contained in:
commit
9184300398
@ -65,7 +65,7 @@ function setupAngularRoutes($routeProvider, $locationProvider) {
|
||||
})
|
||||
.when("/dashboard/import", {
|
||||
templateUrl:
|
||||
"public/app/features/dashboard/partials/dashboardImport.html",
|
||||
"public/app/features/dashboard/partials/dashboard_import.html",
|
||||
controller: "DashboardImportCtrl",
|
||||
controllerAs: "ctrl"
|
||||
})
|
||||
|
@ -251,7 +251,7 @@ export class BackendSrv {
|
||||
createDashboardFolder(name) {
|
||||
const dash = {
|
||||
schemaVersion: 16,
|
||||
title: name,
|
||||
title: name.trim(),
|
||||
editable: true,
|
||||
panels: []
|
||||
};
|
||||
|
@ -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 { DashboardListCtrl } from "./dashboard_list_ctrl";
|
||||
|
@ -3,23 +3,22 @@ import appEvents from "app/core/app_events";
|
||||
export class CreateFolderCtrl {
|
||||
title = "";
|
||||
navModel: any;
|
||||
nameExists = false;
|
||||
titleTouched = false;
|
||||
hasValidationError: boolean;
|
||||
validationError: any;
|
||||
|
||||
/** @ngInject **/
|
||||
constructor(private backendSrv, private $location, navModelSrv) {
|
||||
this.navModel = navModelSrv.getNav("dashboards", "manage-dashboards", 0);
|
||||
constructor(private backendSrv, private $location, private validationSrv, navModelSrv) {
|
||||
this.navModel = navModelSrv.getNav('dashboards', 'manage-dashboards', 0);
|
||||
}
|
||||
|
||||
create() {
|
||||
if (!this.title || this.title.trim().length === 0) {
|
||||
if (this.hasValidationError) {
|
||||
return;
|
||||
}
|
||||
|
||||
const title = this.title.trim();
|
||||
|
||||
return this.backendSrv.createDashboardFolder(title).then(result => {
|
||||
appEvents.emit("alert-success", ["Folder Created", "OK"]);
|
||||
return this.backendSrv.createDashboardFolder(this.title).then(result => {
|
||||
appEvents.emit('alert-success', ['Folder Created', 'OK']);
|
||||
|
||||
var folderUrl = `/dashboards/folder/${result.dashboard.id}/${
|
||||
result.meta.slug
|
||||
@ -31,14 +30,13 @@ export class CreateFolderCtrl {
|
||||
titleChanged() {
|
||||
this.titleTouched = true;
|
||||
|
||||
this.backendSrv.search({ query: this.title }).then(res => {
|
||||
this.nameExists = false;
|
||||
for (let hit of res) {
|
||||
if (this.title === hit.title) {
|
||||
this.nameExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
this.validationSrv.validateNewDashboardOrFolderName(this.title)
|
||||
.then(() => {
|
||||
this.hasValidationError = false;
|
||||
})
|
||||
.catch(err => {
|
||||
this.hasValidationError = true;
|
||||
this.validationError = err.message;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -13,16 +13,13 @@ export class DashboardImportCtrl {
|
||||
gnetUrl: string;
|
||||
gnetError: string;
|
||||
gnetInfo: any;
|
||||
titleTouched: boolean;
|
||||
hasNameValidationError: boolean;
|
||||
nameValidationError: any;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(
|
||||
private backendSrv,
|
||||
navModelSrv,
|
||||
private $location,
|
||||
private $scope,
|
||||
$routeParams
|
||||
) {
|
||||
this.navModel = navModelSrv.getNav("create", "import");
|
||||
constructor(private backendSrv, private validationSrv, navModelSrv, private $location, private $scope, $routeParams) {
|
||||
this.navModel = navModelSrv.getNav('create', 'import');
|
||||
|
||||
this.step = 1;
|
||||
this.nameExists = false;
|
||||
@ -93,15 +90,21 @@ export class DashboardImportCtrl {
|
||||
}
|
||||
|
||||
titleChanged() {
|
||||
this.backendSrv.search({ query: this.dash.title }).then(res => {
|
||||
this.nameExists = false;
|
||||
for (let hit of res) {
|
||||
if (this.dash.title === hit.title) {
|
||||
this.titleTouched = true;
|
||||
this.nameExists = false;
|
||||
|
||||
this.validationSrv.validateNewDashboardOrFolderName(this.dash.title)
|
||||
.then(() => {
|
||||
this.hasNameValidationError = false;
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.type === 'EXISTING') {
|
||||
this.nameExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.hasNameValidationError = true;
|
||||
this.nameValidationError = err.message;
|
||||
});
|
||||
}
|
||||
|
||||
saveDashboard() {
|
||||
|
@ -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> 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>
|
170
public/app/features/dashboard/folder_picker/folder_picker.ts
Normal file
170
public/app/features/dashboard/folder_picker/folder_picker.ts
Normal file
@ -0,0 +1,170 @@
|
||||
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);
|
@ -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);
|
@ -18,12 +18,15 @@
|
||||
<folder-picker
|
||||
on-load="ctrl.onFolderChange($folder)"
|
||||
on-change="ctrl.onFolderChange($folder)"
|
||||
enter-folder-creation="ctrl.onEnterFolderCreation()"
|
||||
exit-folder-creation="ctrl.onExitFolderCreation()"
|
||||
enable-create-new="true"
|
||||
label-class="width-7">
|
||||
</folder-picker>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gf-form-button-row text-center">
|
||||
<button type="submit" class="btn btn-success" ng-disabled="ctrl.saveForm.$invalid">Move</button>
|
||||
<button type="submit" class="btn btn-success" ng-disabled="ctrl.saveForm.$invalid || !ctrl.isValidFolderSelection">Move</button>
|
||||
<a class="btn-text" ng-click="ctrl.dismiss();">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -6,6 +6,7 @@ export class MoveToFolderCtrl {
|
||||
folder: any;
|
||||
dismiss: any;
|
||||
afterSave: any;
|
||||
isValidFolderSelection = true;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private backendSrv) {}
|
||||
@ -39,6 +40,14 @@ export class MoveToFolderCtrl {
|
||||
return this.afterSave();
|
||||
});
|
||||
}
|
||||
|
||||
onEnterFolderCreation() {
|
||||
this.isValidFolderSelection = false;
|
||||
}
|
||||
|
||||
onExitFolderCreation() {
|
||||
this.isValidFolderSelection = true;
|
||||
}
|
||||
}
|
||||
|
||||
export function moveToFolderModal() {
|
||||
|
@ -7,34 +7,25 @@
|
||||
<form name="ctrl.saveForm" ng-submit="ctrl.create()" novalidate>
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form gf-form--grow">
|
||||
<label class="gf-form-label width-10">Folder name</label>
|
||||
<input type="text" class="gf-form-input max-width-25" ng-model="ctrl.title" give-focus="true" ng-change="ctrl.titleChanged()" ng-model-options="{ debounce: 400 }" ng-class="{'validation-error': ctrl.nameExists || !ctrl.dash.title}">
|
||||
<label class="gf-form-label text-success" ng-if="!ctrl.nameExists && ctrl.title">
|
||||
<label class="gf-form-label width-10">Name</label>
|
||||
<input type="text" class="gf-form-input" ng-model="ctrl.title" give-focus="true" ng-change="ctrl.titleChanged()" ng-model-options="{ debounce: 400 }" ng-class="{'validation-error': ctrl.nameExists || !ctrl.dash.title}">
|
||||
<label class="gf-form-label text-success" ng-if="ctrl.titleTouched && !ctrl.hasValidationError">
|
||||
<i class="fa fa-check"></i>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-inline" ng-if="ctrl.nameExists">
|
||||
<div class="gf-form-inline" ng-if="ctrl.hasValidationError">
|
||||
<div class="gf-form offset-width-10 gf-form--grow">
|
||||
<label class="gf-form-label text-warning gf-form-label--grow">
|
||||
<i class="fa fa-warning"></i>
|
||||
A Folder or Dashboard with the same name already exists
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-inline" ng-if="!ctrl.title && ctrl.titleTouched">
|
||||
<div class="gf-form offset-width-10 gf-form--grow">
|
||||
<label class="gf-form-label text-warning gf-form-label--grow">
|
||||
<i class="fa fa-warning"></i>
|
||||
A Folder should have a name
|
||||
{{ctrl.validationError}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-button-row">
|
||||
<button type="submit" class="btn btn-success width-12" ng-disabled="ctrl.nameExists || ctrl.title.length === 0">
|
||||
<button type="submit" class="btn btn-success width-12" ng-disabled="!ctrl.titleTouched || ctrl.hasValidationError">
|
||||
<i class="fa fa-save"></i> Create
|
||||
</button>
|
||||
</div>
|
||||
|
@ -65,26 +65,17 @@
|
||||
<div class="gf-form gf-form--grow">
|
||||
<label class="gf-form-label width-15">Name</label>
|
||||
<input type="text" class="gf-form-input" ng-model="ctrl.dash.title" give-focus="true" ng-change="ctrl.titleChanged()" ng-class="{'validation-error': ctrl.nameExists || !ctrl.dash.title}">
|
||||
<label class="gf-form-label text-success" ng-if="!ctrl.nameExists && ctrl.dash.title">
|
||||
<label class="gf-form-label text-success" ng-if="ctrl.titleTouched && !ctrl.hasNameValidationError">
|
||||
<i class="fa fa-check"></i>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-inline" ng-if="ctrl.nameExists">
|
||||
<div class="gf-form-inline" ng-if="ctrl.hasNameValidationError">
|
||||
<div class="gf-form offset-width-15 gf-form--grow">
|
||||
<label class="gf-form-label text-warning gf-form-label--grow">
|
||||
<i class="fa fa-warning"></i>
|
||||
A Dashboard with the same name already exists
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-inline" ng-if="!ctrl.dash.title">
|
||||
<div class="gf-form offset-width-15 gf-form--grow">
|
||||
<label class="gf-form-label text-warning gf-form-label--grow">
|
||||
<i class="fa fa-warning"></i>
|
||||
A Dashboard should have a name
|
||||
{{ctrl.nameValidationError}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
@ -22,13 +22,16 @@ const template = `
|
||||
<div class="gf-form">
|
||||
<folder-picker initial-folder-id="ctrl.folderId"
|
||||
on-change="ctrl.onFolderChange($folder)"
|
||||
enter-folder-creation="ctrl.onEnterFolderCreation()"
|
||||
exit-folder-creation="ctrl.onExitFolderCreation()"
|
||||
enable-create-new="true"
|
||||
label-class="width-7">
|
||||
</folder-picker>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-button-row text-center">
|
||||
<button type="submit" class="btn btn-success" ng-disabled="ctrl.saveForm.$invalid">Save</button>
|
||||
<button type="submit" class="btn btn-success" ng-disabled="ctrl.saveForm.$invalid || !ctrl.isValidFolderSelection">Save</button>
|
||||
<a class="btn-text" ng-click="ctrl.dismiss();">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
@ -38,6 +41,7 @@ const template = `
|
||||
export class SaveDashboardAsModalCtrl {
|
||||
clone: any;
|
||||
folderId: any;
|
||||
isValidFolderSelection = true;
|
||||
dismiss: () => void;
|
||||
|
||||
/** @ngInject */
|
||||
@ -68,8 +72,16 @@ export class SaveDashboardAsModalCtrl {
|
||||
return this.dashboardSrv.save(this.clone).then(this.dismiss);
|
||||
}
|
||||
|
||||
onEnterFolderCreation() {
|
||||
this.isValidFolderSelection = false;
|
||||
}
|
||||
|
||||
onExitFolderCreation() {
|
||||
this.isValidFolderSelection = true;
|
||||
}
|
||||
|
||||
keyDown(evt) {
|
||||
if (evt.keyCode === 13) {
|
||||
if (this.isValidFolderSelection && evt.keyCode === 13) {
|
||||
this.save();
|
||||
}
|
||||
}
|
||||
|
@ -45,9 +45,11 @@
|
||||
</bootstrap-tagsinput>
|
||||
</div>
|
||||
<folder-picker initial-title="ctrl.dashboard.meta.folderTitle"
|
||||
initial-folder-id="ctrl.dashboard.meta.folderId"
|
||||
on-change="ctrl.onFolderChange($folder)"
|
||||
label-class="width-7">
|
||||
initial-folder-id="ctrl.dashboard.meta.folderId"
|
||||
on-change="ctrl.onFolderChange($folder)"
|
||||
enable-create-new="true"
|
||||
is-valid-selection="true"
|
||||
label-class="width-7">
|
||||
</folder-picker>
|
||||
<gf-form-switch class="gf-form" label="Editable" tooltip="Uncheck, then save and reload to disable all dashboard editing" checked="ctrl.dashboard.editable" label-class="width-7">
|
||||
</gf-form-switch>
|
||||
|
@ -6,6 +6,7 @@ describe("DashboardImportCtrl", function() {
|
||||
|
||||
let navModelSrv;
|
||||
let backendSrv;
|
||||
let validationSrv;
|
||||
|
||||
beforeEach(() => {
|
||||
navModelSrv = {
|
||||
@ -17,7 +18,11 @@ describe("DashboardImportCtrl", function() {
|
||||
get: jest.fn()
|
||||
};
|
||||
|
||||
ctx.ctrl = new DashboardImportCtrl(backendSrv, navModelSrv, {}, {}, {});
|
||||
validationSrv = {
|
||||
validateNewDashboardOrFolderName: jest.fn().mockReturnValue(Promise.resolve())
|
||||
};
|
||||
|
||||
ctx.ctrl = new DashboardImportCtrl(backendSrv, validationSrv, navModelSrv, {}, {}, {});
|
||||
});
|
||||
|
||||
describe("when uploading json", function() {
|
||||
|
46
public/app/features/dashboard/validation_srv.ts
Normal file
46
public/app/features/dashboard/validation_srv.ts
Normal file
@ -0,0 +1,46 @@
|
||||
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 {
|
||||
|
Loading…
Reference in New Issue
Block a user