diff --git a/pkg/services/search/models.go b/pkg/services/search/models.go index dbd4f21a71e..cf510ed8462 100644 --- a/pkg/services/search/models.go +++ b/pkg/services/search/models.go @@ -15,6 +15,7 @@ type Hit struct { Id int64 `json:"id"` Title string `json:"title"` Uri string `json:"uri"` + Slug string `json:"slug"` Type HitType `json:"type"` Tags []string `json:"tags"` IsStarred bool `json:"isStarred"` diff --git a/pkg/services/sqlstore/dashboard.go b/pkg/services/sqlstore/dashboard.go index af27dad59de..31a42a7b3b3 100644 --- a/pkg/services/sqlstore/dashboard.go +++ b/pkg/services/sqlstore/dashboard.go @@ -260,6 +260,7 @@ func makeQueryResult(query *search.FindPersistedDashboardsQuery, res []Dashboard Id: item.Id, Title: item.Title, Uri: "db/" + item.Slug, + Slug: item.Slug, Type: getHitType(item), FolderId: item.FolderId, FolderTitle: item.FolderTitle, diff --git a/public/app/core/components/search/search_results.html b/public/app/core/components/search/search_results.html index 324654b9815..8cf5505186b 100644 --- a/public/app/core/components/search/search_results.html +++ b/public/app/core/components/search/search_results.html @@ -1,21 +1,21 @@
- +
+ ng-show="ctrl.editable" + on-change="ctrl.selectionChanged($event)" + checked="section.checked" + switch-class="gf-form-switch--transparent gf-form-switch--search-result__section">
{{::section.title}} -
-   -
- +
+ + + - +
@@ -23,10 +23,10 @@
+ ng-show="ctrl.editable" + on-change="ctrl.selectionChanged()" + checked="item.checked" + switch-class="gf-form-switch--transparent gf-form-switch--search-result__item">
@@ -34,9 +34,6 @@
{{::item.title}}
-
- {{::item.folderTitle}} -
diff --git a/public/app/core/routes/routes.ts b/public/app/core/routes/routes.ts index 3f6d4916e77..1f5b8cacf0e 100644 --- a/public/app/core/routes/routes.ts +++ b/public/app/core/routes/routes.ts @@ -78,17 +78,17 @@ function setupAngularRoutes($routeProvider, $locationProvider) { controller : 'CreateFolderCtrl', controllerAs: 'ctrl', }) - .when('/dashboards/folder/:folderId/:type/:slug/permissions', { + .when('/dashboards/folder/:folderId/:slug/permissions', { templateUrl: 'public/app/features/dashboard/partials/folder_permissions.html', controller : 'FolderPermissionsCtrl', controllerAs: 'ctrl', }) - .when('/dashboards/folder/:folderId/:type/:slug/settings', { + .when('/dashboards/folder/:folderId/:slug/settings', { templateUrl: 'public/app/features/dashboard/partials/folder_settings.html', controller : 'FolderSettingsCtrl', controllerAs: 'ctrl', }) - .when('/dashboards/folder/:folderId/:type/:slug', { + .when('/dashboards/folder/:folderId/:slug', { templateUrl: 'public/app/features/dashboard/partials/folder_dashboards.html', controller : 'FolderDashboardsCtrl', controllerAs: 'ctrl', diff --git a/public/app/core/services/search_srv.ts b/public/app/core/services/search_srv.ts index 52f008e1812..69341757a45 100644 --- a/public/app/core/services/search_srv.ts +++ b/public/app/core/services/search_srv.ts @@ -84,105 +84,96 @@ export class SearchSrv { }); } - private getDashboardsAndFolders(sections) { - const rootFolderId = 0; - - let query = { - folderIds: [rootFolderId], - }; - - return this.backendSrv.search(query).then(results => { - for (let hit of results) { - if (hit.type === 'dash-folder') { - sections[hit.id] = { - id: hit.id, - title: hit.title, - items: [], - icon: 'fa fa-folder', - score: _.keys(sections).length, - uri: hit.uri, - toggle: this.toggleFolder.bind(this), - }; - } - } - - sections[0] = { - id: 0, - title: 'Root', - items: [], - icon: 'fa fa-folder-open', - score: _.keys(sections).length, - expanded: true, - }; - - for (let hit of results) { - if (hit.type === 'dash-folder') { - continue; - } - let section = sections[hit.folderId || 0]; - if (section) { - section.items.push(this.transformToViewModel(hit)); - } else { - console.log('Error: dashboard returned from browse search but not folder', hit.id, hit.folderId); - } - } - }); + private transformToViewModel(hit) { + hit.url = 'dashboard/db/' + hit.slug; + return hit; } - private browse(options) { + search(options) { let sections: any = {}; - let promises = []; + let query = _.clone(options); + let hasFilters = options.query || + (options.tag && options.tag.length > 0) || options.starred || + (options.folderIds && options.folderIds.length > 0); - if (!options.skipRecent) { + if (!options.skipRecent && !hasFilters) { promises.push(this.getRecentDashboards(sections)); } - if (!options.skipStarred) { + if (!options.skipStarred && !hasFilters) { promises.push(this.getStarred(sections)); } - promises.push(this.getDashboardsAndFolders(sections)); + query.folderIds = query.folderIds || []; + if (!hasFilters) { + query.folderIds = [0]; + } + + promises.push(this.backendSrv.search(query).then(results => { + return this.handleSearchResult(sections, results); + })); return this.$q.all(promises).then(() => { return _.sortBy(_.values(sections), 'score'); }); } - private transformToViewModel(hit) { - hit.url = 'dashboard/' + hit.uri; - return hit; - } - - search(options) { - if (!options.folderIds && !options.query && (!options.tag || options.tag.length === 0) && !options.starred) { - return this.browse(options); + private handleSearchResult(sections, results) { + if (results.length === 0) { + return sections; } - let query = _.clone(options); - query.folderIds = options.folderIds || []; - query.type = 'dash-db'; + // create folder index + for (let hit of results) { + if (hit.type === 'dash-folder') { + sections[hit.id] = { + id: hit.id, + title: hit.title, + expanded: false, + items: [], + toggle: this.toggleFolder.bind(this), + url: `dashboards/folder/${hit.id}/${hit.slug}`, + icon: 'fa fa-folder', + score: _.keys(sections).length, + }; + } + } - return this.backendSrv.search(query).then(results => { - if (results.length === 0) { - return results; + for (let hit of results) { + if (hit.type === 'dash-folder') { + continue; } - let section = { - hideHeader: true, - items: [], - expanded: true, - }; - - for (let hit of results) { - if (hit.type === 'dash-folder') { - continue; + let section = sections[hit.folderId || 0]; + if (!section) { + if (hit.folderId) { + section = { + id: hit.folderId, + title: hit.folderTitle, + url: `dashboards/folder/${hit.folderId}/${hit.folderSlug}`, + items: [], + icon: 'fa fa-folder-open', + toggle: this.toggleFolder.bind(this), + score: _.keys(sections).length, + }; + } else { + section = { + id: 0, + title: 'Root', + items: [], + icon: 'fa fa-folder-open', + toggle: this.toggleFolder.bind(this), + score: _.keys(sections).length, + }; } - section.items.push(this.transformToViewModel(hit)); + // add section + sections[hit.folderId || 0] = section; } - return [section]; - }); + section.expanded = true; + section.items.push(this.transformToViewModel(hit)); + } } private toggleFolder(section) { diff --git a/public/app/core/specs/search_srv.jest.ts b/public/app/core/specs/search_srv.jest.ts index 649cfbf00dd..f3135aa5f58 100644 --- a/public/app/core/specs/search_srv.jest.ts +++ b/public/app/core/specs/search_srv.jest.ts @@ -214,9 +214,8 @@ describe('SearchSrv', () => { expect(backendSrvMock.search.mock.calls[0][0].folderIds).toHaveLength(0); }); - it('should place all results in a single section', () => { - expect(results).toHaveLength(1); - expect(results[0].hideHeader).toBe(true); + it('should group results by folder', () => { + expect(results).toHaveLength(2); }); }); diff --git a/public/app/features/dashboard/folder_dashboards_ctrl.ts b/public/app/features/dashboard/folder_dashboards_ctrl.ts index afe7685f5a0..66358fed49d 100644 --- a/public/app/features/dashboard/folder_dashboards_ctrl.ts +++ b/public/app/features/dashboard/folder_dashboards_ctrl.ts @@ -6,10 +6,12 @@ export class FolderDashboardsCtrl { /** @ngInject */ constructor(private backendSrv, navModelSrv, private $routeParams) { - if (this.$routeParams.folderId && this.$routeParams.type && this.$routeParams.slug) { + if (this.$routeParams.folderId && this.$routeParams.slug) { this.folderId = $routeParams.folderId; - new FolderPageLoader(this.backendSrv, this.$routeParams).load(this, this.folderId, 'manage-folder-dashboards'); + const loader = new FolderPageLoader(this.backendSrv, this.$routeParams); + + loader.load(this, this.folderId, 'manage-folder-dashboards'); } } } diff --git a/public/app/features/dashboard/folder_page_loader.ts b/public/app/features/dashboard/folder_page_loader.ts index 23917aac085..0db18fc44a2 100644 --- a/public/app/features/dashboard/folder_page_loader.ts +++ b/public/app/features/dashboard/folder_page_loader.ts @@ -9,7 +9,7 @@ export class FolderPageLoader { icon: 'fa fa-folder-open', id: 'manage-folder', subTitle: 'Manage folder dashboards & permissions', - url: '/fsdfds', + url: '', text: '', breadcrumbs: [ { title: 'Dashboards', url: '/dashboards' }, @@ -41,11 +41,11 @@ export class FolderPageLoader { } }; - return this.backendSrv.getDashboard(this.$routeParams.type, this.$routeParams.slug).then(result => { + return this.backendSrv.getDashboard('db', this.$routeParams.slug).then(result => { const folderTitle = result.dashboard.title; ctrl.navModel.main.text = ''; ctrl.navModel.main.breadcrumbs = [ - { title: 'Dashboards', uri: '/dashboards' }, + { title: 'Dashboards', url: '/dashboards' }, { title: folderTitle } ]; @@ -65,6 +65,6 @@ export class FolderPageLoader { } createFolderUrl(folderId: number, type: string, slug: string) { - return `/dashboards/folder/${folderId}/${type}/${slug}`; + return `/dashboards/folder/${folderId}/${slug}`; } } diff --git a/public/app/features/dashboard/folder_permissions_ctrl.ts b/public/app/features/dashboard/folder_permissions_ctrl.ts index dcc8fe02d6a..d24330399e6 100644 --- a/public/app/features/dashboard/folder_permissions_ctrl.ts +++ b/public/app/features/dashboard/folder_permissions_ctrl.ts @@ -6,7 +6,7 @@ export class FolderPermissionsCtrl { /** @ngInject */ constructor(private backendSrv, navModelSrv, private $routeParams) { - if (this.$routeParams.folderId && this.$routeParams.type && this.$routeParams.slug) { + if (this.$routeParams.folderId && this.$routeParams.slug) { this.folderId = $routeParams.folderId; new FolderPageLoader(this.backendSrv, this.$routeParams).load(this, this.folderId, 'manage-folder-permissions'); diff --git a/public/app/features/dashboard/folder_settings_ctrl.ts b/public/app/features/dashboard/folder_settings_ctrl.ts index 7d846f8ec60..a187606567e 100644 --- a/public/app/features/dashboard/folder_settings_ctrl.ts +++ b/public/app/features/dashboard/folder_settings_ctrl.ts @@ -11,7 +11,7 @@ export class FolderSettingsCtrl { /** @ngInject */ constructor(private backendSrv, navModelSrv, private $routeParams, private $location) { - if (this.$routeParams.folderId && this.$routeParams.type && this.$routeParams.slug) { + if (this.$routeParams.folderId && this.$routeParams.slug) { this.folderId = $routeParams.folderId; this.folderPageLoader = new FolderPageLoader(this.backendSrv, this.$routeParams); diff --git a/public/app/features/plugins/partials/ds_edit.html b/public/app/features/plugins/partials/ds_edit.html index 53a7405e943..69a649b132b 100644 --- a/public/app/features/plugins/partials/ds_edit.html +++ b/public/app/features/plugins/partials/ds_edit.html @@ -65,11 +65,11 @@
- + - Cancel + Back

diff --git a/public/sass/components/_search.scss b/public/sass/components/_search.scss index 6fae1889c24..bc10f034b68 100644 --- a/public/sass/components/_search.scss +++ b/public/sass/components/_search.scss @@ -137,6 +137,12 @@ &:hover, &.selected { color: $text-color; } + + &:hover { + .search-section__header__link { + opacity: 1; + } + } } .search-section__header__icon { @@ -154,6 +160,13 @@ line-height: 24px; } +.search-section__header__link { + padding: 2px 10px 0; + color: $text-muted; + opacity: 0; + transition: opacity 150ms ease-in-out; +} + .search-item { @include list-item(); @@ -181,12 +194,6 @@ color: $list-item-link-color; } -.search-item__body-sub-title { - color: $text-muted; - font-size: $font-size-sm; - line-height: 9pt; -} - .search-item__icon { padding: 5px; flex: 0 0 auto;