dashboard_folders: refactoring picker and folder selection in dashboard settings & save as menu
This commit is contained in:
+30
-18
@@ -40,8 +40,8 @@ func GetDashboard(c *middleware.Context) Response {
|
|||||||
slug := strings.ToLower(c.Params(":slug"))
|
slug := strings.ToLower(c.Params(":slug"))
|
||||||
|
|
||||||
query := m.GetDashboardQuery{Slug: slug, OrgId: c.OrgId}
|
query := m.GetDashboardQuery{Slug: slug, OrgId: c.OrgId}
|
||||||
err := bus.Dispatch(&query)
|
|
||||||
if err != nil {
|
if err := bus.Dispatch(&query); err != nil {
|
||||||
return ApiError(404, "Dashboard not found", err)
|
return ApiError(404, "Dashboard not found", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,27 +70,39 @@ func GetDashboard(c *middleware.Context) Response {
|
|||||||
creator = getUserLogin(dash.CreatedBy)
|
creator = getUserLogin(dash.CreatedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
meta := dtos.DashboardMeta{
|
||||||
|
IsStarred: isStarred,
|
||||||
|
Slug: slug,
|
||||||
|
Type: m.DashTypeDB,
|
||||||
|
CanStar: c.IsSignedIn,
|
||||||
|
CanSave: canSave,
|
||||||
|
CanEdit: canEdit,
|
||||||
|
Created: dash.Created,
|
||||||
|
Updated: dash.Updated,
|
||||||
|
UpdatedBy: updater,
|
||||||
|
CreatedBy: creator,
|
||||||
|
Version: dash.Version,
|
||||||
|
HasAcl: dash.HasAcl,
|
||||||
|
IsFolder: dash.IsFolder,
|
||||||
|
FolderId: dash.ParentId,
|
||||||
|
FolderTitle: "Root",
|
||||||
|
}
|
||||||
|
|
||||||
|
// lookup folder title
|
||||||
|
if dash.ParentId > 0 {
|
||||||
|
query := m.GetDashboardQuery{Id: dash.ParentId, OrgId: c.OrgId}
|
||||||
|
if err := bus.Dispatch(&query); err != nil {
|
||||||
|
return ApiError(500, "Dashboard folder could not be read", err)
|
||||||
|
}
|
||||||
|
meta.FolderTitle = query.Result.Title
|
||||||
|
}
|
||||||
|
|
||||||
// make sure db version is in sync with json model version
|
// make sure db version is in sync with json model version
|
||||||
dash.Data.Set("version", dash.Version)
|
dash.Data.Set("version", dash.Version)
|
||||||
|
|
||||||
dto := dtos.DashboardFullWithMeta{
|
dto := dtos.DashboardFullWithMeta{
|
||||||
Dashboard: dash.Data,
|
Dashboard: dash.Data,
|
||||||
Meta: dtos.DashboardMeta{
|
Meta: meta,
|
||||||
IsStarred: isStarred,
|
|
||||||
Slug: slug,
|
|
||||||
Type: m.DashTypeDB,
|
|
||||||
CanStar: c.IsSignedIn,
|
|
||||||
CanSave: canSave,
|
|
||||||
CanEdit: canEdit,
|
|
||||||
Created: dash.Created,
|
|
||||||
Updated: dash.Updated,
|
|
||||||
UpdatedBy: updater,
|
|
||||||
CreatedBy: creator,
|
|
||||||
Version: dash.Version,
|
|
||||||
HasAcl: dash.HasAcl,
|
|
||||||
IsFolder: dash.IsFolder,
|
|
||||||
ParentId: dash.ParentId,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.TimeRequest(metrics.M_Api_Dashboard_Get)
|
c.TimeRequest(metrics.M_Api_Dashboard_Get)
|
||||||
|
|||||||
+18
-17
@@ -7,23 +7,24 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type DashboardMeta struct {
|
type DashboardMeta struct {
|
||||||
IsStarred bool `json:"isStarred,omitempty"`
|
IsStarred bool `json:"isStarred,omitempty"`
|
||||||
IsHome bool `json:"isHome,omitempty"`
|
IsHome bool `json:"isHome,omitempty"`
|
||||||
IsSnapshot bool `json:"isSnapshot,omitempty"`
|
IsSnapshot bool `json:"isSnapshot,omitempty"`
|
||||||
Type string `json:"type,omitempty"`
|
Type string `json:"type,omitempty"`
|
||||||
CanSave bool `json:"canSave"`
|
CanSave bool `json:"canSave"`
|
||||||
CanEdit bool `json:"canEdit"`
|
CanEdit bool `json:"canEdit"`
|
||||||
CanStar bool `json:"canStar"`
|
CanStar bool `json:"canStar"`
|
||||||
Slug string `json:"slug"`
|
Slug string `json:"slug"`
|
||||||
Expires time.Time `json:"expires"`
|
Expires time.Time `json:"expires"`
|
||||||
Created time.Time `json:"created"`
|
Created time.Time `json:"created"`
|
||||||
Updated time.Time `json:"updated"`
|
Updated time.Time `json:"updated"`
|
||||||
UpdatedBy string `json:"updatedBy"`
|
UpdatedBy string `json:"updatedBy"`
|
||||||
CreatedBy string `json:"createdBy"`
|
CreatedBy string `json:"createdBy"`
|
||||||
Version int `json:"version"`
|
Version int `json:"version"`
|
||||||
HasAcl bool `json:"hasAcl"`
|
HasAcl bool `json:"hasAcl"`
|
||||||
IsFolder bool `json:"isFolder"`
|
IsFolder bool `json:"isFolder"`
|
||||||
ParentId int64 `json:"parentId"`
|
FolderId int64 `json:"folderId"`
|
||||||
|
FolderTitle string `json:"folderTitle"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DashboardFullWithMeta struct {
|
type DashboardFullWithMeta struct {
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ export class NavModelSrv {
|
|||||||
clickHandler: () => dashNavCtrl.showHelpModal()
|
clickHandler: () => dashNavCtrl.showHelpModal()
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.contextSrv.isEditor) {
|
if (this.contextSrv.isEditor && !dashboard.meta.isFolder) {
|
||||||
menu.push({
|
menu.push({
|
||||||
title: 'Save As ...',
|
title: 'Save As ...',
|
||||||
icon: 'fa fa-fw fa-save',
|
icon: 'fa fa-fw fa-save',
|
||||||
|
|||||||
@@ -128,8 +128,10 @@ export class DashboardCtrl {
|
|||||||
$rootScope.$broadcast("refresh");
|
$rootScope.$broadcast("refresh");
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.onFolderChange = function(parentId) {
|
$scope.onFolderChange = function(folder) {
|
||||||
$scope.dashboard.parentId = parentId;
|
$scope.dashboard.parentId = folder.id;
|
||||||
|
$scope.dashboard.meta.folderId = folder.id;
|
||||||
|
$scope.dashboard.meta.folderTitle= folder.title;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -113,17 +113,8 @@ export class DashboardSrv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
showSaveAsModal() {
|
showSaveAsModal() {
|
||||||
var newScope = this.$rootScope.$new();
|
|
||||||
newScope.clone = this.dash.getSaveModelClone();
|
|
||||||
newScope.clone.editable = true;
|
|
||||||
newScope.clone.hideControls = false;
|
|
||||||
newScope.clone.meta = {};
|
|
||||||
newScope.clone.meta.parentId = this.dash.meta.parentId;
|
|
||||||
newScope.clone.meta.isFolder = this.dash.meta.isFolder;
|
|
||||||
|
|
||||||
this.$rootScope.appEvent('show-modal', {
|
this.$rootScope.appEvent('show-modal', {
|
||||||
templateHtml: '<save-dashboard-as-modal dismiss="dismiss()"></save-dashboard-as-modal>',
|
templateHtml: '<save-dashboard-as-modal dismiss="dismiss()"></save-dashboard-as-modal>',
|
||||||
scope: newScope,
|
|
||||||
modalClass: 'modal--narrow'
|
modalClass: 'modal--narrow'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -131,7 +122,6 @@ export class DashboardSrv {
|
|||||||
showSaveModal() {
|
showSaveModal() {
|
||||||
this.$rootScope.appEvent('show-modal', {
|
this.$rootScope.appEvent('show-modal', {
|
||||||
templateHtml: '<save-dashboard-modal dismiss="dismiss()"></save-dashboard-modal>',
|
templateHtml: '<save-dashboard-modal dismiss="dismiss()"></save-dashboard-modal>',
|
||||||
scope: this.$rootScope.$new(),
|
|
||||||
modalClass: 'modal--narrow'
|
modalClass: 'modal--narrow'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
<div class="gf-form">
|
|
||||||
<label class="gf-form-label width-7">Folder</label>
|
|
||||||
<div class="dropdown">
|
|
||||||
<metric-segment segment="ctrl.selectedFolderSegment"
|
|
||||||
get-options="ctrl.getOptions()"
|
|
||||||
on-change="ctrl.folderChanged()"></metric-segment>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -6,47 +6,38 @@ import _ from 'lodash';
|
|||||||
|
|
||||||
export class FolderPickerCtrl {
|
export class FolderPickerCtrl {
|
||||||
folders: Folder[];
|
folders: Folder[];
|
||||||
selectedFolder: number;
|
selectedOption: any;
|
||||||
selectedFolderSegment: any;
|
initialTitle: string;
|
||||||
onChange: any;
|
onChange: any;
|
||||||
rootFolderName: string;
|
labelClass: string;
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor(private backendSrv, private $scope, private $sce, private uiSegmentSrv) {
|
constructor(private backendSrv, private $scope, private $sce) {
|
||||||
this.selectedFolderSegment = this.uiSegmentSrv.newSegment({value: this.rootFolderName || 'Root', selectMode: true});
|
if (!this.labelClass) {
|
||||||
this.get();
|
this.labelClass = "width-7";
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selectedOption = {text: this.initialTitle, value: null};
|
||||||
}
|
}
|
||||||
|
|
||||||
get() {
|
getOptions(query) {
|
||||||
var params = {
|
var params = {
|
||||||
|
query: query,
|
||||||
type: 'dash-folder',
|
type: 'dash-folder',
|
||||||
};
|
};
|
||||||
|
|
||||||
return this.backendSrv.search(params).then(result => {
|
return this.backendSrv.search(params).then(result => {
|
||||||
this.folders = [{id: 0, title: this.rootFolderName || 'Root', type: 'dash-folder'}];
|
if (query === "") {
|
||||||
this.folders.push(...result);
|
result.unshift({title: "Root", value: 0});
|
||||||
|
|
||||||
if (this.selectedFolder) {
|
|
||||||
const selected = _.find(this.folders, {id: this.selectedFolder});
|
|
||||||
|
|
||||||
this.selectedFolderSegment.value = selected.title;
|
|
||||||
this.selectedFolderSegment.text = selected.title;
|
|
||||||
this.selectedFolderSegment.html = this.$sce.trustAsHtml(selected.title);
|
|
||||||
}
|
}
|
||||||
|
return _.map(result, item => {
|
||||||
|
return {text: item.title, value: item.id};
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getOptions() {
|
folderChanged(option) {
|
||||||
return Promise.resolve(this.folders.map(folder => {
|
this.onChange({$folder: {id: option.value, title: option.text}});
|
||||||
return this.uiSegmentSrv.newSegment(folder.title);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
folderChanged() {
|
|
||||||
const selected = _.find(this.folders, {title: this.selectedFolderSegment.value});
|
|
||||||
if (selected) {
|
|
||||||
this.onChange({$folderId: selected.id});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,17 +52,29 @@ export interface Folder {
|
|||||||
dashboards?: any;
|
dashboards?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const template = `
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label {{ctrl.labelClass}}">Folder</label>
|
||||||
|
<div class="dropdown">
|
||||||
|
<gf-form-dropdown model="ctrl.selectedOption"
|
||||||
|
get-options="ctrl.getOptions($query)"
|
||||||
|
on-change="ctrl.folderChanged($option)">
|
||||||
|
</gf-form-dropdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
export function folderPicker() {
|
export function folderPicker() {
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
templateUrl: 'public/app/features/dashboard/folder_picker/picker.html',
|
template: template,
|
||||||
controller: FolderPickerCtrl,
|
controller: FolderPickerCtrl,
|
||||||
bindToController: true,
|
bindToController: true,
|
||||||
controllerAs: 'ctrl',
|
controllerAs: 'ctrl',
|
||||||
scope: {
|
scope: {
|
||||||
selectedFolder: "<",
|
initialTitle: "<",
|
||||||
onChange: "&",
|
onChange: "&",
|
||||||
rootFolderName: "@"
|
labelClass: "@",
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<folder-picker ng-if="!dashboardMeta.isFolder" selected-folder="dashboardMeta.parentId" on-change="onFolderChange($folderId)"></folder-picker>
|
<folder-picker ng-if="!dashboardMeta.isFolder"
|
||||||
|
initial-title="dashboardMeta.folderTitle"
|
||||||
|
on-change="onFolderChange($folder)"
|
||||||
|
label-class="width-7">
|
||||||
|
</folder-picker>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="section">
|
<div class="section">
|
||||||
|
|||||||
@@ -18,11 +18,13 @@ const template = `
|
|||||||
<form name="ctrl.saveForm" ng-submit="ctrl.save()" class="modal-content" novalidate>
|
<form name="ctrl.saveForm" ng-submit="ctrl.save()" class="modal-content" novalidate>
|
||||||
<div class="p-t-2">
|
<div class="p-t-2">
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
<label class="gf-form-label">New name</label>
|
<label class="gf-form-label width-7">New name</label>
|
||||||
<input type="text" class="gf-form-input" ng-model="ctrl.clone.title" give-focus="true" required>
|
<input type="text" class="gf-form-input" ng-model="ctrl.clone.title" give-focus="true" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
<folder-picker ng-if="!ctrl.clone.meta.isFolder" selected-folder="ctrl.clone.meta.parentId" on-change="ctrl.onFolderChange($folderId)">
|
<folder-picker initial-title="ctrl.folderTitle"
|
||||||
|
on-change="ctrl.onFolderChange($folder)"
|
||||||
|
label-class="width-7">
|
||||||
</folder-picker>
|
</folder-picker>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -37,6 +39,7 @@ const template = `
|
|||||||
|
|
||||||
export class SaveDashboardAsModalCtrl {
|
export class SaveDashboardAsModalCtrl {
|
||||||
clone: any;
|
clone: any;
|
||||||
|
folderTitle: any;
|
||||||
dismiss: () => void;
|
dismiss: () => void;
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
@@ -47,6 +50,7 @@ export class SaveDashboardAsModalCtrl {
|
|||||||
this.clone.title += ' Copy';
|
this.clone.title += ' Copy';
|
||||||
this.clone.editable = true;
|
this.clone.editable = true;
|
||||||
this.clone.hideControls = false;
|
this.clone.hideControls = false;
|
||||||
|
this.folderTitle = dashboard.meta.folderTitle || 'Root';
|
||||||
|
|
||||||
// remove alerts
|
// remove alerts
|
||||||
this.clone.rows.forEach(row => {
|
this.clone.rows.forEach(row => {
|
||||||
@@ -68,8 +72,8 @@ export class SaveDashboardAsModalCtrl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onFolderChange(parentId) {
|
onFolderChange(folder) {
|
||||||
this.clone.parentId = parentId;
|
this.clone.parentId = folder.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,8 +23,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
<span class="gf-form-label width-6">Folder</span>
|
<folder-picker initial-text="ctrl.folderTitle"
|
||||||
<folder-picker selected-folder="ctrl.panel.folderId" on-change="ctrl.onFolderChange" root-folder-name="All"></folder-picker>
|
on-change="ctrl.onFolderChange($folder)"
|
||||||
|
label-class="width-6">
|
||||||
|
</folder-picker>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class DashListCtrl extends PanelCtrl {
|
|||||||
|
|
||||||
groups: any[];
|
groups: any[];
|
||||||
modes: any[];
|
modes: any[];
|
||||||
|
folderTitle: any;
|
||||||
|
|
||||||
panelDefaults = {
|
panelDefaults = {
|
||||||
query: '',
|
query: '',
|
||||||
@@ -19,7 +20,7 @@ class DashListCtrl extends PanelCtrl {
|
|||||||
search: false,
|
search: false,
|
||||||
starred: true,
|
starred: true,
|
||||||
headings: true,
|
headings: true,
|
||||||
folderId: 0
|
folderId: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
@@ -65,6 +66,10 @@ class DashListCtrl extends PanelCtrl {
|
|||||||
this.editorTabIndex = 1;
|
this.editorTabIndex = 1;
|
||||||
this.modes = ['starred', 'search', 'recently viewed'];
|
this.modes = ['starred', 'search', 'recently viewed'];
|
||||||
this.addEditorTab('Options', 'public/app/plugins/panel/dashlist/editor.html');
|
this.addEditorTab('Options', 'public/app/plugins/panel/dashlist/editor.html');
|
||||||
|
|
||||||
|
if (!this.panel.folderId) {
|
||||||
|
this.folderTitle = "All";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onRefresh() {
|
onRefresh() {
|
||||||
@@ -126,9 +131,10 @@ class DashListCtrl extends PanelCtrl {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onFolderChange(parentId) {
|
onFolderChange(folder) {
|
||||||
this.$scope.$parent.ctrl.panel.folderId = parentId;
|
this.panel.folderId = folder.id;
|
||||||
this.$scope.$parent.ctrl.refresh();
|
this.panel.folderTitle = folder.title;
|
||||||
|
this.refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user