Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
nexpie-grafana-theme
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Registry
Registry
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kornkitt Poolsup
nexpie-grafana-theme
Commits
2ea663df
Commit
2ea663df
authored
Dec 07, 2017
by
Marcus Efraimsson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
dashfolders: New Dashboard Folder page
Fixes #10083. Permissions page is just a placeholder for now.
parent
10b0fc79
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
437 additions
and
329 deletions
+437
-329
pkg/api/index.go
+15
-0
public/app/core/components/PageHeader/PageHeader.tsx
+19
-1
public/app/core/components/manage_dashboards/manage_dashboards.html
+86
-108
public/app/core/components/manage_dashboards/manage_dashboards.ts
+221
-0
public/app/core/components/search/search.html
+6
-6
public/app/core/components/search/search_results.html
+1
-1
public/app/core/core.ts
+3
-1
public/app/core/routes/routes.ts
+8
-3
public/app/core/specs/manage_dashboards.jest.ts
+3
-3
public/app/features/dashboard/all.ts
+4
-0
public/app/features/dashboard/dashboard_list_ctrl.ts
+1
-206
public/app/features/dashboard/folder_dashboards_ctrl.ts
+16
-0
public/app/features/dashboard/folder_page_loader.ts
+21
-0
public/app/features/dashboard/folder_permissions_ctrl.ts
+16
-0
public/app/features/dashboard/partials/dashboard_list.html
+5
-0
public/app/features/dashboard/partials/folder_dashboards.html
+6
-0
public/app/features/dashboard/partials/folder_permissions.html
+6
-0
No files found.
pkg/api/index.go
View file @
2ea663df
...
...
@@ -118,6 +118,21 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
Children
:
dashboardChildNavs
,
})
dashboardFolderChildNavs
:=
[]
*
dtos
.
NavLink
{
{
Text
:
"Dashboards"
,
Id
:
"manage-folder-dashboards"
,
Url
:
setting
.
AppSubUrl
+
"/dashboards"
,
Icon
:
"fa fa-fw fa-th-large"
},
{
Text
:
"Permissions"
,
Id
:
"manage-folder-permissions"
,
Url
:
setting
.
AppSubUrl
+
"/dashboards?1"
,
Icon
:
"fa fa-fw fa-lock"
},
}
data
.
NavTree
=
append
(
data
.
NavTree
,
&
dtos
.
NavLink
{
Text
:
"Dashboards"
,
Id
:
"manage-folder"
,
SubTitle
:
"Manage folder dashboards & permissions"
,
Icon
:
"fa fa-folder-open"
,
Url
:
setting
.
AppSubUrl
+
"/"
,
HideFromMenu
:
true
,
Children
:
dashboardFolderChildNavs
,
})
if
c
.
IsSignedIn
{
profileNode
:=
&
dtos
.
NavLink
{
Text
:
c
.
SignedInUser
.
Name
,
...
...
public/app/core/components/PageHeader/PageHeader.tsx
View file @
2ea663df
...
...
@@ -72,6 +72,19 @@ export default class PageHeader extends React.Component<IProps, any> {
super
(
props
);
}
renderBreadcrumb
(
breadcrumbs
)
{
const
breadcrumbsResult
=
[];
for
(
let
i
=
0
;
i
<
breadcrumbs
.
length
;
i
++
)
{
const
bc
=
breadcrumbs
[
i
];
if
(
bc
.
uri
)
{
breadcrumbsResult
.
push
(<
a
className=
"text-link"
key=
{
i
}
href=
{
bc
.
uri
}
>
{
bc
.
title
}
</
a
>);
}
else
{
breadcrumbsResult
.
push
(<
span
key=
{
i
}
>
/
{
bc
.
title
}
</
span
>);
}
}
return
breadcrumbsResult
;
}
renderHeaderTitle
(
main
)
{
return
(
<
div
className=
"page-header__inner"
>
...
...
@@ -81,7 +94,12 @@ export default class PageHeader extends React.Component<IProps, any> {
</
span
>
<
div
className=
"page-header__info-block"
>
<
h1
className=
"page-header__title"
>
{
main
.
text
}
</
h1
>
{
main
.
text
&&
<
h1
className=
"page-header__title"
>
{
main
.
text
}
</
h1
>
}
{
main
.
breadcrumbs
&&
main
.
breadcrumbs
.
length
>
0
&&
(
<
h1
className=
"page-header__title"
>
{
this
.
renderBreadcrumb
(
main
.
breadcrumbs
)
}
</
h1
>)
}
{
main
.
subTitle
&&
<
div
className=
"page-header__sub-title"
>
{
main
.
subTitle
}
</
div
>
}
{
main
.
subType
&&
(
<
div
className=
"page-header__stamps"
>
...
...
public/app/
features/dashboard/partials/dashboardList
.html
→
public/app/
core/components/manage_dashboards/manage_dashboards
.html
View file @
2ea663df
<page-header
model=
"ctrl.navModel"
></page-header>
<div
class=
"page-container page-body"
>
<div
class=
"page-action-bar"
ng-show=
"ctrl.folderTitle"
>
<div
class=
"gf-form gf-form--grow"
>
<h3
class=
"page-sub-heading"
>
<i
class=
"fa fa-folder-open"
></i>
{{ctrl.folderTitle}}
</h3>
</div>
<div
class=
"page-action-bar__spacer"
></div>
<button
class=
"btn btn-inverse"
disabled
>
Permissions
</button>
<a
class=
"btn btn-success"
href=
"/dashboard/new"
>
<i
class=
"fa fa-plus"
></i>
Dashboard
</a>
<a
class=
"btn btn-success"
href=
"/dashboard/new/?editview=new-folder"
>
<i
class=
"fa fa-plus"
></i>
Folder
</a>
</div>
<div
class=
"page-action-bar"
>
<div
class=
"gf-form gf-form--grow"
>
<label
class=
"gf-form-label"
>
Search
</label>
<input
type=
"text"
class=
"gf-form-input max-width-30"
placeholder=
"Find Dashboard by name"
tabindex=
"1"
give-focus=
"true"
ng-model=
"ctrl.query.query"
ng-model-options=
"{ debounce: 500 }"
spellcheck=
'false'
ng-change=
"ctrl.onQueryChange()"
/>
</div>
<div
class=
"page-action-bar__spacer"
></div>
<a
class=
"btn btn-success"
href=
"/dashboard/new"
ng-hide=
"ctrl.folderTitle"
>
<i
class=
"fa fa-plus"
></i>
Dashboard
</a>
<a
class=
"btn btn-success"
href=
"/dashboard/new/?editview=new-folder"
ng-hide=
"ctrl.folderTitle"
>
<i
class=
"fa fa-plus"
></i>
Folder
</a>
<div
class=
"page-action-bar"
>
<div
class=
"gf-form gf-form--grow"
>
<label
class=
"gf-form-label"
>
Search
</label>
<input
type=
"text"
class=
"gf-form-input max-width-30"
placeholder=
"Find Dashboard by name"
tabindex=
"1"
give-focus=
"true"
ng-model=
"ctrl.query.query"
ng-model-options=
"{ debounce: 500 }"
spellcheck=
'false'
ng-change=
"ctrl.onQueryChange()"
/>
</div>
<div
class=
"page-action-bar__spacer"
></div>
<a
class=
"btn btn-success"
href=
"/dashboard/new"
>
<i
class=
"fa fa-plus"
></i>
Dashboard
</a>
<a
class=
"btn btn-success"
href=
"/dashboard/new/?editview=new-folder"
ng-if=
"!ctrl.folderId"
>
<i
class=
"fa fa-plus"
></i>
Folder
</a>
</div>
<div
class=
"gf-form"
ng-if=
"ctrl.query.tag.length"
>
Filters:
<span
ng-repeat=
"tagName in ctrl.query.tag"
>
<div
class=
"gf-form"
ng-if=
"ctrl.query.tag.length"
>
Filters:
<span
ng-repeat=
"tagName in ctrl.query.tag"
>
<a
ng-click=
"ctrl.removeTag(tagName, $event)"
tag-color-from-name=
"tagName"
class=
"label label-tag"
>
<i
class=
"fa fa-remove"
></i>
{{tagName}}
<i
class=
"fa fa-remove"
></i>
{{tagName}}
</a>
</span>
</div>
<div
class=
"gf-form"
>
<div
class=
"gf-form-button-row"
ng-show=
"ctrl.hasFilters"
>
<button
type=
"button"
class=
"btn gf-form-button btn-inverse btn-small"
ng-click=
"ctrl.clearFilters()"
>
<i
class=
"fa fa-close"
></i>
Clear current search query and filters
</button>
</div>
</div>
</span>
</div>
<div
ng-if=
"!ctrl.hasFilters && ctrl.sections.length === 0"
>
<empty-list-cta
model=
"{
title: 'This folder doesn\'t have any dashboards yet',
buttonIcon: 'gicon gicon-dashboard-new',
buttonLink: '/dashboard/new',
buttonTitle: 'Create Dashboard',
proTip: 'You can bulk move dashboards into this folder from the main dashboard list.',
proTipLink: 'http://docs.grafana.org/administration/provisioning/#datasources?utm_source=grafana_ds_list',
proTipLinkTitle: 'Learn more',
proTipTarget: '_blank'
}"
/>
<div
class=
"gf-form"
>
<div
class=
"gf-form-button-row"
ng-show=
"ctrl.hasFilters"
>
<button
type=
"button"
class=
"btn gf-form-button btn-inverse btn-small"
ng-click=
"ctrl.clearFilters()"
>
<i
class=
"fa fa-close"
></i>
Clear current search query and filters
</button>
</div>
</div>
<div
class=
"dashboard-list"
ng-show=
"ctrl.sections.length > 0"
>
<div
class=
"search-results-filter-row"
>
<gf-form-switch
on-change=
"ctrl.onSelectAllChanged()"
checked=
"ctrl.selectAllChecked"
switch-class=
"gf-form-switch--transparent gf-form-switch--search-result-filter-row__checkbox"
<div
class=
"dashboard-list"
ng-show=
"ctrl.sections.length > 0"
>
<div
class=
"search-results-filter-row"
>
<gf-form-switch
on-change=
"ctrl.onSelectAllChanged()"
checked=
"ctrl.selectAllChecked"
switch-class=
"gf-form-switch--transparent gf-form-switch--search-result-filter-row__checkbox"
/>
<div
class=
"search-results-filter-row__filters"
>
<select
class=
"search-results-filter-row__filters-item gf-form-input"
ng-model=
"ctrl.selectedStarredFilter"
ng-options=
"t.text disable when t.disabled for t in ctrl.starredFilterOptions"
ng-change=
"ctrl.onStarredFilterChange()"
ng-show=
"!(ctrl.canMove || ctrl.canDelete)"
/>
<div
class=
"search-results-filter-row__filters"
>
<select
class=
"search-results-filter-row__filters-item gf-form-input"
ng-model=
"ctrl.selectedStarredFilter"
ng-options=
"t.text disable when t.disabled for t in ctrl.starredFilterOptions"
ng-change=
"ctrl.onStarredFilterChange()"
ng-show=
"!(ctrl.canMove || ctrl.canDelete)"
/>
<select
class=
"search-results-filter-row__filters-item gf-form-input"
ng-model=
"ctrl.selectedTagFilter"
ng-options=
"t.term disable when t.disabled for t in ctrl.tagFilterOptions"
ng-change=
"ctrl.onTagFilterChange()"
ng-show=
"!(ctrl.canMove || ctrl.canDelete)"
/>
<div
class=
"gf-form-button-row"
ng-show=
"ctrl.canMove || ctrl.canDelete"
>
<button
type=
"button"
class=
"btn gf-form-button btn-inverse"
ng-disabled=
"!ctrl.canMove"
ng-click=
"ctrl.moveTo()"
bs-tooltip=
"ctrl.canMove ? '' : 'Select a dashboard to move (cannot move folders)'"
data-placement=
"bottom"
>
<i
class=
"fa fa-exchange"
></i>
Move
</button>
<button
type=
"button"
class=
"btn gf-form-button btn-danger"
ng-click=
"ctrl.delete()"
ng-disabled=
"!ctrl.canDelete"
>
<i
class=
"fa fa-trash"
></i>
Delete
</button>
</div>
<select
class=
"search-results-filter-row__filters-item gf-form-input"
ng-model=
"ctrl.selectedTagFilter"
ng-options=
"t.term disable when t.disabled for t in ctrl.tagFilterOptions"
ng-change=
"ctrl.onTagFilterChange()"
ng-show=
"!(ctrl.canMove || ctrl.canDelete)"
/>
<div
class=
"gf-form-button-row"
ng-show=
"ctrl.canMove || ctrl.canDelete"
>
<button
type=
"button"
class=
"btn gf-form-button btn-inverse"
ng-disabled=
"!ctrl.canMove"
ng-click=
"ctrl.moveTo()"
bs-tooltip=
"ctrl.canMove ? '' : 'Select a dashboard to move (cannot move folders)'"
data-placement=
"bottom"
>
<i
class=
"fa fa-exchange"
></i>
Move
</button>
<button
type=
"button"
class=
"btn gf-form-button btn-danger"
ng-click=
"ctrl.delete()"
ng-disabled=
"!ctrl.canDelete"
>
<i
class=
"fa fa-trash"
></i>
Delete
</button>
</div>
</div>
<div
class=
"search-results-container"
>
</div>
<div
class=
"search-results-container"
>
<dashboard-search-results
results=
"ctrl.sections"
editable=
"true"
on-selection-changed=
"ctrl.selectionChanged()"
on-tag-selected=
"ctrl.filterByTag($tag)"
/>
</div>
results=
"ctrl.sections"
editable=
"true"
on-selection-changed=
"ctrl.selectionChanged()"
on-tag-selected=
"ctrl.filterByTag($tag)"
/>
</div>
</div>
<div
ng-if=
"ctrl.folderId && !ctrl.hasFilters && ctrl.sections.length === 0"
>
<empty-list-cta
model=
"{
title: 'This folder doesn\'t have any dashboards yet',
buttonIcon: 'gicon gicon-dashboard-new',
buttonLink: '/dashboard/new',
buttonTitle: 'Create Dashboard',
proTip: 'You can bulk move dashboards into this folder from the main dashboard list.',
proTipLink: 'http://docs.grafana.org/administration/provisioning/#datasources?utm_source=grafana_ds_list',
proTipLinkTitle: 'Learn more',
proTipTarget: '_blank'
}"
/>
</div>
\ No newline at end of file
public/app/core/components/manage_dashboards/manage_dashboards.ts
0 → 100644
View file @
2ea663df
import
_
from
'lodash'
;
import
coreModule
from
'app/core/core_module'
;
import
appEvents
from
'app/core/app_events'
;
import
{
SearchSrv
}
from
'app/core/services/search_srv'
;
export
class
ManageDashboardsCtrl
{
public
sections
:
any
[];
tagFilterOptions
:
any
[];
selectedTagFilter
:
any
;
query
:
any
;
navModel
:
any
;
canDelete
=
false
;
canMove
=
false
;
hasFilters
=
false
;
selectAllChecked
=
false
;
starredFilterOptions
=
[{
text
:
'Filter by Starred'
,
disabled
:
true
},
{
text
:
'Yes'
},
{
text
:
'No'
}];
selectedStarredFilter
:
any
;
folderId
?:
number
;
/** @ngInject */
constructor
(
private
backendSrv
,
navModelSrv
,
private
$q
,
private
searchSrv
:
SearchSrv
)
{
this
.
query
=
{
query
:
''
,
mode
:
'tree'
,
tag
:
[],
starred
:
false
,
skipRecent
:
true
,
skipStarred
:
true
};
if
(
this
.
folderId
)
{
this
.
query
.
folderIds
=
[
this
.
folderId
];
}
this
.
selectedStarredFilter
=
this
.
starredFilterOptions
[
0
];
this
.
getDashboards
().
then
(()
=>
{
this
.
getTags
();
});
}
getDashboards
()
{
return
this
.
searchSrv
.
search
(
this
.
query
).
then
((
result
)
=>
{
return
this
.
initDashboardList
(
result
);
});
}
initDashboardList
(
result
:
any
)
{
this
.
canMove
=
false
;
this
.
canDelete
=
false
;
this
.
selectAllChecked
=
false
;
this
.
hasFilters
=
this
.
query
.
query
.
length
>
0
||
this
.
query
.
tag
.
length
>
0
||
this
.
query
.
starred
;
if
(
!
result
)
{
this
.
sections
=
[];
return
;
}
this
.
sections
=
result
;
for
(
let
section
of
this
.
sections
)
{
section
.
checked
=
false
;
for
(
let
dashboard
of
section
.
items
)
{
dashboard
.
checked
=
false
;
}
}
}
selectionChanged
()
{
let
selectedDashboards
=
0
;
for
(
let
section
of
this
.
sections
)
{
selectedDashboards
+=
_
.
filter
(
section
.
items
,
{
checked
:
true
}).
length
;
}
const
selectedFolders
=
_
.
filter
(
this
.
sections
,
{
checked
:
true
}).
length
;
this
.
canMove
=
selectedDashboards
>
0
&&
selectedFolders
===
0
;
this
.
canDelete
=
selectedDashboards
>
0
||
selectedFolders
>
0
;
}
getDashboardsToDelete
()
{
let
selectedDashboards
=
[];
for
(
const
section
of
this
.
sections
)
{
if
(
section
.
checked
)
{
selectedDashboards
.
push
(
section
.
uri
);
}
else
{
const
selected
=
_
.
filter
(
section
.
items
,
{
checked
:
true
});
selectedDashboards
.
push
(...
_
.
map
(
selected
,
'uri'
));
}
}
return
selectedDashboards
;
}
getFolderIds
(
sections
)
{
const
ids
=
[];
for
(
let
s
of
sections
)
{
if
(
s
.
checked
)
{
ids
.
push
(
s
.
id
);
}
}
return
ids
;
}
delete
()
{
const
selectedDashboards
=
this
.
getDashboardsToDelete
();
appEvents
.
emit
(
'confirm-modal'
,
{
title
:
'Delete'
,
text
:
`Do you want to delete the
${
selectedDashboards
.
length
}
selected dashboards?`
,
icon
:
'fa-trash'
,
yesText
:
'Delete'
,
onConfirm
:
()
=>
{
const
promises
=
[];
for
(
let
dash
of
selectedDashboards
)
{
promises
.
push
(
this
.
backendSrv
.
delete
(
`/api/dashboards/
${
dash
}
`
));
}
this
.
$q
.
all
(
promises
).
then
(()
=>
{
this
.
getDashboards
();
});
}
});
}
getDashboardsToMove
()
{
let
selectedDashboards
=
[];
for
(
const
section
of
this
.
sections
)
{
const
selected
=
_
.
filter
(
section
.
items
,
{
checked
:
true
});
selectedDashboards
.
push
(...
_
.
map
(
selected
,
'uri'
));
}
return
selectedDashboards
;
}
moveTo
()
{
const
selectedDashboards
=
this
.
getDashboardsToMove
();
const
template
=
'<move-to-folder-modal dismiss="dismiss()" '
+
'dashboards="model.dashboards" after-save="model.afterSave()">'
+
'</move-to-folder-modal>`'
;
appEvents
.
emit
(
'show-modal'
,
{
templateHtml
:
template
,
modalClass
:
'modal--narrow'
,
model
:
{
dashboards
:
selectedDashboards
,
afterSave
:
this
.
getDashboards
.
bind
(
this
)
}
});
}
getTags
()
{
return
this
.
searchSrv
.
getDashboardTags
().
then
((
results
)
=>
{
this
.
tagFilterOptions
=
[{
term
:
'Filter By Tag'
,
disabled
:
true
}].
concat
(
results
);
this
.
selectedTagFilter
=
this
.
tagFilterOptions
[
0
];
});
}
filterByTag
(
tag
)
{
if
(
_
.
indexOf
(
this
.
query
.
tag
,
tag
)
===
-
1
)
{
this
.
query
.
tag
.
push
(
tag
);
}
return
this
.
getDashboards
();
}
onQueryChange
()
{
return
this
.
getDashboards
();
}
onTagFilterChange
()
{
var
res
=
this
.
filterByTag
(
this
.
selectedTagFilter
.
term
);
this
.
selectedTagFilter
=
this
.
tagFilterOptions
[
0
];
return
res
;
}
removeTag
(
tag
,
evt
)
{
this
.
query
.
tag
=
_
.
without
(
this
.
query
.
tag
,
tag
);
this
.
getDashboards
();
if
(
evt
)
{
evt
.
stopPropagation
();
evt
.
preventDefault
();
}
}
onStarredFilterChange
()
{
this
.
query
.
starred
=
this
.
selectedStarredFilter
.
text
===
'Yes'
;
return
this
.
getDashboards
();
}
onSelectAllChanged
()
{
for
(
let
section
of
this
.
sections
)
{
if
(
!
section
.
hideHeader
)
{
section
.
checked
=
this
.
selectAllChecked
;
}
section
.
items
=
_
.
map
(
section
.
items
,
(
item
)
=>
{
item
.
checked
=
this
.
selectAllChecked
;
return
item
;
});
}
this
.
selectionChanged
();
}
clearFilters
()
{
this
.
query
.
query
=
''
;
this
.
query
.
tag
=
[];
this
.
query
.
starred
=
false
;
this
.
getDashboards
();
}
}
export
function
manageDashboardsDirective
()
{
return
{
restrict
:
'E'
,
templateUrl
:
'public/app/core/components/manage_dashboards/manage_dashboards.html'
,
controller
:
ManageDashboardsCtrl
,
bindToController
:
true
,
controllerAs
:
'ctrl'
,
scope
:
{
folderId
:
'='
}
};
}
coreModule
.
directive
(
'manageDashboards'
,
manageDashboardsDirective
);
public/app/core/components/search/search.html
View file @
2ea663df
...
...
@@ -21,12 +21,12 @@
<div
class=
"search-dropdown"
>
<div
class=
"search-dropdown__col_1"
>
<div
class=
"search-results-container"
grafana-scrollbar
>
<h6
ng-show=
"!ctrl.isLoading && ctrl.results.length === 0"
>
No dashboards matching your query were found.
</h6>
<dashboard-search-results
results=
"ctrl.results"
on-tag-selected=
"ctrl.filterByTag($tag)"
on-folder-expanding=
"ctrl.folderExpanding()"
on-folder-expanded=
"ctrl.folderExpanded($folder)"
/>
<h6
ng-show=
"!ctrl.isLoading && ctrl.results.length === 0"
>
No dashboards matching your query were found.
</h6>
<dashboard-search-results
results=
"ctrl.results"
on-tag-selected=
"ctrl.filterByTag($tag)"
on-folder-expanding=
"ctrl.folderExpanding()"
on-folder-expanded=
"ctrl.folderExpanded($folder)"
/>
</div>
</div>
...
...
public/app/core/components/search/search_results.html
View file @
2ea663df
...
...
@@ -10,7 +10,7 @@
</div>
<i
class=
"search-section__header__icon"
ng-class=
"section.icon"
></i>
<span
class=
"search-section__header__text"
>
{{::section.title}}
</span>
<div
ng-show=
"ctrl.editable && section.id > 0
&& section.expanded
"
ng-click=
"ctrl.navigateToFolder(section, $event)"
>
<div
ng-show=
"ctrl.editable && section.id > 0"
ng-click=
"ctrl.navigateToFolder(section, $event)"
>
<i
class=
"fa fa-cog search-section__header__toggle"
></i>
</div>
<i
class=
"fa fa-minus search-section__header__toggle"
ng-show=
"section.expanded"
></i>
...
...
public/app/core/core.ts
View file @
2ea663df
...
...
@@ -53,6 +53,7 @@ import {orgSwitcher} from './components/org_switcher';
import
{
profiler
}
from
'./profiler'
;
import
{
registerAngularDirectives
}
from
'./angular_wrappers'
;
import
{
searchResultsDirective
}
from
'./components/search/search_results'
;
import
{
manageDashboardsDirective
}
from
'./components/manage_dashboards/manage_dashboards'
;
export
{
profiler
,
...
...
@@ -85,5 +86,6 @@ export {
geminiScrollbar
,
gfPageDirective
,
orgSwitcher
,
searchResultsDirective
searchResultsDirective
,
manageDashboardsDirective
};
public/app/core/routes/routes.ts
View file @
2ea663df
...
...
@@ -69,13 +69,18 @@ function setupAngularRoutes($routeProvider, $locationProvider) {
controllerAs
:
'ctrl'
,
})
.
when
(
'/dashboards'
,
{
templateUrl
:
'public/app/features/dashboard/partials/dashboard
L
ist.html'
,
templateUrl
:
'public/app/features/dashboard/partials/dashboard
_l
ist.html'
,
controller
:
'DashboardListCtrl'
,
controllerAs
:
'ctrl'
,
})
.
when
(
'/dashboards/folder/:folderId/:type/:slug/permissions'
,
{
templateUrl
:
'public/app/features/dashboard/partials/folder_permissions.html'
,
controller
:
'FolderPermissionsCtrl'
,
controllerAs
:
'ctrl'
,
})
.
when
(
'/dashboards/folder/:folderId/:type/:slug'
,
{
templateUrl
:
'public/app/features/dashboard/partials/
dashboardList
.html'
,
controller
:
'
DashboardList
Ctrl'
,
templateUrl
:
'public/app/features/dashboard/partials/
folder_dashboards
.html'
,
controller
:
'
FolderDashboards
Ctrl'
,
controllerAs
:
'ctrl'
,
})
.
when
(
'/org'
,
{
...
...
public/app/
features/dashboard/specs/dashboard_list_ctrl
.jest.ts
→
public/app/
core/specs/manage_dashboards
.jest.ts
View file @
2ea663df
import
{
DashboardListCtrl
}
from
'../dashboard_list_ctrl
'
;
import
{
ManageDashboardsCtrl
}
from
'app/core/components/manage_dashboards/manage_dashboards
'
;
import
{
SearchSrv
}
from
'app/core/services/search_srv'
;
import
q
from
'q'
;
describe
(
'
DashboardListCtrl
'
,
()
=>
{
describe
(
'
ManageDashboards
'
,
()
=>
{
let
ctrl
;
describe
(
'when browsing dashboards'
,
()
=>
{
...
...
@@ -542,5 +542,5 @@ function createCtrlWithStubs(searchResponse: any, tags?: any) {
}
};
return
new
DashboardListCtrl
({},
{
getNav
:
()
=>
{
}
},
q
,
<
SearchSrv
>
searchSrvStub
,
{}
);
return
new
ManageDashboardsCtrl
({},
{
getNav
:
()
=>
{
}
},
q
,
<
SearchSrv
>
searchSrvStub
);
}
public/app/features/dashboard/all.ts
View file @
2ea663df
...
...
@@ -29,7 +29,11 @@ import './move_to_folder_modal/move_to_folder';
import
coreModule
from
'app/core/core_module'
;
import
{
DashboardListCtrl
}
from
'./dashboard_list_ctrl'
;
import
{
FolderDashboardsCtrl
}
from
'./folder_dashboards_ctrl'
;
import
{
FolderPermissionsCtrl
}
from
'./folder_permissions_ctrl'
;
import
{
DashboardImportCtrl
}
from
'./dashboard_import_ctrl'
;
coreModule
.
controller
(
'DashboardListCtrl'
,
DashboardListCtrl
);
coreModule
.
controller
(
'FolderDashboardsCtrl'
,
FolderDashboardsCtrl
);
coreModule
.
controller
(
'FolderPermissionsCtrl'
,
FolderPermissionsCtrl
);
coreModule
.
controller
(
'DashboardImportCtrl'
,
DashboardImportCtrl
);
public/app/features/dashboard/dashboard_list_ctrl.ts
View file @
2ea663df
import
_
from
'lodash'
;
import
appEvents
from
'app/core/app_events'
;
import
{
SearchSrv
}
from
'app/core/services/search_srv'
;
export
class
DashboardListCtrl
{
public
sections
:
any
[];
tagFilterOptions
:
any
[];
selectedTagFilter
:
any
;
query
:
any
;
navModel
:
any
;
canDelete
=
false
;
canMove
=
false
;
hasFilters
=
false
;
selectAllChecked
=
false
;
starredFilterOptions
=
[{
text
:
'Filter by Starred'
,
disabled
:
true
},
{
text
:
'Yes'
},
{
text
:
'No'
}];
selectedStarredFilter
:
any
;
folderTitle
=
null
;
/** @ngInject */
constructor
(
private
backendSrv
,
navModelSrv
,
private
$q
,
private
searchSrv
:
SearchSrv
,
private
$routeParams
)
{
constructor
(
navModelSrv
)
{
this
.
navModel
=
navModelSrv
.
getNav
(
'dashboards'
,
'manage-dashboards'
,
0
);
this
.
query
=
{
query
:
''
,
mode
:
'tree'
,
tag
:
[],
starred
:
false
,
skipRecent
:
true
,
skipStarred
:
true
};
this
.
selectedStarredFilter
=
this
.
starredFilterOptions
[
0
];
if
(
this
.
$routeParams
.
folderId
&&
this
.
$routeParams
.
type
&&
this
.
$routeParams
.
slug
)
{
backendSrv
.
getDashboard
(
this
.
$routeParams
.
type
,
this
.
$routeParams
.
slug
).
then
(
result
=>
{
this
.
folderTitle
=
result
.
dashboard
.
title
;
this
.
query
.
folderIds
=
[
result
.
dashboard
.
id
];
this
.
getDashboards
().
then
(()
=>
{
this
.
getTags
();
});
});
}
else
{
this
.
getDashboards
().
then
(()
=>
{
this
.
getTags
();
});
}
}
getDashboards
()
{
return
this
.
searchSrv
.
search
(
this
.
query
).
then
((
result
)
=>
{
return
this
.
initDashboardList
(
result
);
});
}
initDashboardList
(
result
:
any
)
{
this
.
canMove
=
false
;
this
.
canDelete
=
false
;
this
.
selectAllChecked
=
false
;
this
.
hasFilters
=
this
.
query
.
query
.
length
>
0
||
this
.
query
.
tag
.
length
>
0
||
this
.
query
.
starred
;
if
(
!
result
)
{
this
.
sections
=
[];
return
;
}
this
.
sections
=
result
;
for
(
let
section
of
this
.
sections
)
{
section
.
checked
=
false
;
for
(
let
dashboard
of
section
.
items
)
{
dashboard
.
checked
=
false
;
}
}
}
selectionChanged
()
{
let
selectedDashboards
=
0
;
for
(
let
section
of
this
.
sections
)
{
selectedDashboards
+=
_
.
filter
(
section
.
items
,
{
checked
:
true
}).
length
;
}
const
selectedFolders
=
_
.
filter
(
this
.
sections
,
{
checked
:
true
}).
length
;
this
.
canMove
=
selectedDashboards
>
0
&&
selectedFolders
===
0
;
this
.
canDelete
=
selectedDashboards
>
0
||
selectedFolders
>
0
;
}
getDashboardsToDelete
()
{
let
selectedDashboards
=
[];
for
(
const
section
of
this
.
sections
)
{
if
(
section
.
checked
)
{
selectedDashboards
.
push
(
section
.
uri
);
}
else
{
const
selected
=
_
.
filter
(
section
.
items
,
{
checked
:
true
});
selectedDashboards
.
push
(...
_
.
map
(
selected
,
'uri'
));
}
}
return
selectedDashboards
;
}
getFolderIds
(
sections
)
{
const
ids
=
[];
for
(
let
s
of
sections
)
{
if
(
s
.
checked
)
{
ids
.
push
(
s
.
id
);
}
}
return
ids
;
}
delete
()
{
const
selectedDashboards
=
this
.
getDashboardsToDelete
();
appEvents
.
emit
(
'confirm-modal'
,
{
title
:
'Delete'
,
text
:
`Do you want to delete the
${
selectedDashboards
.
length
}
selected dashboards?`
,
icon
:
'fa-trash'
,
yesText
:
'Delete'
,
onConfirm
:
()
=>
{
const
promises
=
[];
for
(
let
dash
of
selectedDashboards
)
{
promises
.
push
(
this
.
backendSrv
.
delete
(
`/api/dashboards/
${
dash
}
`
));
}
this
.
$q
.
all
(
promises
).
then
(()
=>
{
this
.
getDashboards
();
});
}
});
}
getDashboardsToMove
()
{
let
selectedDashboards
=
[];
for
(
const
section
of
this
.
sections
)
{
const
selected
=
_
.
filter
(
section
.
items
,
{
checked
:
true
});
selectedDashboards
.
push
(...
_
.
map
(
selected
,
'uri'
));
}
return
selectedDashboards
;
}
moveTo
()
{
const
selectedDashboards
=
this
.
getDashboardsToMove
();
const
template
=
'<move-to-folder-modal dismiss="dismiss()" '
+
'dashboards="model.dashboards" after-save="model.afterSave()">'
+
'</move-to-folder-modal>`'
;
appEvents
.
emit
(
'show-modal'
,
{
templateHtml
:
template
,
modalClass
:
'modal--narrow'
,
model
:
{
dashboards
:
selectedDashboards
,
afterSave
:
this
.
getDashboards
.
bind
(
this
)}
});
}
getTags
()
{
return
this
.
searchSrv
.
getDashboardTags
().
then
((
results
)
=>
{
this
.
tagFilterOptions
=
[{
term
:
'Filter By Tag'
,
disabled
:
true
}].
concat
(
results
);
this
.
selectedTagFilter
=
this
.
tagFilterOptions
[
0
];
});
}
filterByTag
(
tag
)
{
if
(
_
.
indexOf
(
this
.
query
.
tag
,
tag
)
===
-
1
)
{
this
.
query
.
tag
.
push
(
tag
);
}
return
this
.
getDashboards
();
}
onQueryChange
()
{
return
this
.
getDashboards
();
}
onTagFilterChange
()
{
var
res
=
this
.
filterByTag
(
this
.
selectedTagFilter
.
term
);
this
.
selectedTagFilter
=
this
.
tagFilterOptions
[
0
];
return
res
;
}
removeTag
(
tag
,
evt
)
{
this
.
query
.
tag
=
_
.
without
(
this
.
query
.
tag
,
tag
);
this
.
getDashboards
();
if
(
evt
)
{
evt
.
stopPropagation
();
evt
.
preventDefault
();
}
}
onStarredFilterChange
()
{
this
.
query
.
starred
=
this
.
selectedStarredFilter
.
text
===
'Yes'
;
return
this
.
getDashboards
();
}
onSelectAllChanged
()
{
for
(
let
section
of
this
.
sections
)
{
if
(
!
section
.
hideHeader
)
{
section
.
checked
=
this
.
selectAllChecked
;
}
section
.
items
=
_
.
map
(
section
.
items
,
(
item
)
=>
{
item
.
checked
=
this
.
selectAllChecked
;
return
item
;
});
}
this
.
selectionChanged
();
}
clearFilters
()
{
this
.
query
.
query
=
''
;
this
.
query
.
tag
=
[];
this
.
query
.
starred
=
false
;
this
.
getDashboards
();
}
}
public/app/features/dashboard/folder_dashboards_ctrl.ts
0 → 100644
View file @
2ea663df
import
{
FolderPageLoader
}
from
'./folder_page_loader'
;
export
class
FolderDashboardsCtrl
{
navModel
:
any
;
folderId
:
number
;
/** @ngInject */
constructor
(
private
backendSrv
,
navModelSrv
,
private
$routeParams
)
{
if
(
this
.
$routeParams
.
folderId
&&
this
.
$routeParams
.
type
&&
this
.
$routeParams
.
slug
)
{
this
.
folderId
=
$routeParams
.
folderId
;
this
.
navModel
=
navModelSrv
.
getNav
(
'manage-folder'
,
'manage-folder-dashboards'
,
0
);
new
FolderPageLoader
(
this
.
backendSrv
,
this
.
$routeParams
).
load
(
this
.
navModel
,
this
.
folderId
);
}
}
}
public/app/features/dashboard/folder_page_loader.ts
0 → 100644
View file @
2ea663df
import
_
from
"lodash"
;
export
class
FolderPageLoader
{
constructor
(
private
backendSrv
,
private
$routeParams
)
{
}
load
(
navModel
,
folderId
)
{
this
.
backendSrv
.
getDashboard
(
this
.
$routeParams
.
type
,
this
.
$routeParams
.
slug
).
then
(
result
=>
{
const
folderTitle
=
result
.
dashboard
.
title
;
navModel
.
main
.
text
=
''
;
navModel
.
main
.
breadcrumbs
=
[
{
title
:
'Dashboards'
,
uri
:
'/dashboards'
},
{
title
:
folderTitle
}
];
const
folderUrl
=
`/dashboards/folder/
${
folderId
}
/
${
result
.
meta
.
type
}
/
${
result
.
meta
.
slug
}
`
;
const
dashTab
=
_
.
find
(
navModel
.
main
.
children
,
{
id
:
'manage-folder-dashboards'
});
dashTab
.
url
=
folderUrl
;
const
permTab
=
_
.
find
(
navModel
.
main
.
children
,
{
id
:
'manage-folder-permissions'
});
permTab
.
url
=
folderUrl
+
'/permissions'
;
});
}
}
public/app/features/dashboard/folder_permissions_ctrl.ts
0 → 100644
View file @
2ea663df
import
{
FolderPageLoader
}
from
'./folder_page_loader'
;
export
class
FolderPermissionsCtrl
{
navModel
:
any
;
folderId
:
number
;
/** @ngInject */
constructor
(
private
backendSrv
,
navModelSrv
,
private
$routeParams
)
{
if
(
this
.
$routeParams
.
folderId
&&
this
.
$routeParams
.
type
&&
this
.
$routeParams
.
slug
)
{
this
.
folderId
=
$routeParams
.
folderId
;
this
.
navModel
=
navModelSrv
.
getNav
(
'manage-folder'
,
'manage-folder-permissions'
,
0
);
new
FolderPageLoader
(
this
.
backendSrv
,
this
.
$routeParams
).
load
(
this
.
navModel
,
this
.
folderId
);
}
}
}
public/app/features/dashboard/partials/dashboard_list.html
0 → 100644
View file @
2ea663df
<page-header
model=
"ctrl.navModel"
></page-header>
<div
class=
"page-container page-body"
>
<manage-dashboards
/>
</div>
public/app/features/dashboard/partials/folder_dashboards.html
0 → 100644
View file @
2ea663df
<page-header
ng-if=
"ctrl.navModel"
model=
"ctrl.navModel"
></page-header>
<div
class=
"page-container page-body"
>
<manage-dashboards
ng-if=
"ctrl.folderId"
folder-id=
"ctrl.folderId"
/>
</div>
\ No newline at end of file
public/app/features/dashboard/partials/folder_permissions.html
0 → 100644
View file @
2ea663df
<page-header
model=
"ctrl.navModel"
></page-header>
<div
class=
"page-container page-body"
>
<h1>
Coming soon! Permissions will be added in Grafana 5.0 beta.
</h1>
</div>
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment