Commit 313735bd by Torkel Ödegaard

search: worked on search results

parent ccbd1800
......@@ -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"`
......
......@@ -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,
......
<div ng-repeat="section in ctrl.results" class="search-section">
<a class="search-section__header pointer" ng-hide="section.hideHeader" ng-class="{'selected': section.selected}" ng-click="ctrl.toggleFolderExpand(section)">
<div class="search-section__header pointer" ng-hide="section.hideHeader" ng-class="{'selected': section.selected}" ng-click="ctrl.toggleFolderExpand(section)">
<div ng-click="ctrl.toggleSelection(section, $event)">
<gf-form-switch
ng-show="ctrl.editable"
on-change="ctrl.selectionChanged($event)"
checked="section.checked"
switch-class="gf-form-switch--transparent gf-form-switch--search-result__section">
ng-show="ctrl.editable"
on-change="ctrl.selectionChanged($event)"
checked="section.checked"
switch-class="gf-form-switch--transparent gf-form-switch--search-result__section">
</gf-form-switch>
</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" ng-click="ctrl.navigateToFolder(section, $event)">
<i class="fa fa-cog search-section__header__toggle"></i>&nbsp;
</div>
<i class="fa fa-angle-down search-section__header__toggle" ng-show="section.expanded"></i>
<a ng-show="section.url" href="{{section.url}}" class="search-section__header__link">
<i class="fa fa-cog"></i>
</a>
<i class="fa fa-angle-down search-section__header__toggle" ng-show="section.expanded"></i>
<i class="fa fa-angle-right search-section__header__toggle" ng-hide="section.expanded"></i>
</a>
</div>
<div class="search-section__header" ng-show="section.hideHeader"></div>
......@@ -23,10 +23,10 @@
<a ng-repeat="item in section.items" class="search-item" ng-class="{'selected': item.selected}" ng-href="{{::item.url}}">
<div ng-click="ctrl.toggleSelection(item, $event)">
<gf-form-switch
ng-show="ctrl.editable"
on-change="ctrl.selectionChanged()"
checked="item.checked"
switch-class="gf-form-switch--transparent gf-form-switch--search-result__item">
ng-show="ctrl.editable"
on-change="ctrl.selectionChanged()"
checked="item.checked"
switch-class="gf-form-switch--transparent gf-form-switch--search-result__item">
</gf-form-switch>
</div>
<span class="search-item__icon">
......@@ -34,9 +34,6 @@
</span>
<span class="search-item__body">
<div class="search-item__body-title">{{::item.title}}</div>
<div class="search-item__body-sub-title" ng-show="item.folderTitle && section.hideHeader">
{{::item.folderTitle}}
</div>
</span>
<span class="search-item__tags">
<span ng-click="ctrl.selectTag(tag, $event)" ng-repeat="tag in item.tags" tag-color-from-name="tag" class="label label-tag">
......
......@@ -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',
......
......@@ -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';
return this.backendSrv.search(query).then(results => {
if (results.length === 0) {
return results;
// 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,
};
}
}
let section = {
hideHeader: true,
items: [],
expanded: true,
};
for (let hit of results) {
if (hit.type === 'dash-folder') {
continue;
}
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) {
......
......@@ -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);
});
});
......
......@@ -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');
}
}
}
......@@ -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}`;
}
}
......@@ -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');
......
......@@ -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);
......
......@@ -65,11 +65,11 @@
</div>
<div class="gf-form-button-row">
<button type="submit" class="btn btn-success" ng-disabled="ctrl.current.readOnly" ng-click="ctrl.saveChanges()">Save</button>
<button type="submit" class="btn btn-success" ng-disabled="ctrl.current.readOnly" ng-click="ctrl.saveChanges()">Save &amp; Test</button>
<button type="submit" class="btn btn-danger" ng-disabled="ctrl.current.readOnly" ng-show="!ctrl.isNew" ng-click="ctrl.delete()">
Delete
</button>
<a class="btn btn-link" href="datasources">Cancel</a>
<a class="btn btn-inverse" href="datasources">Back</a>
</div>
<br />
......
......@@ -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;
......
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