diff --git a/client/app/assets/less/inc/base.less b/client/app/assets/less/inc/base.less
index 8566d47b5..2a4ce1269 100755
--- a/client/app/assets/less/inc/base.less
+++ b/client/app/assets/less/inc/base.less
@@ -85,7 +85,7 @@ strong {
// Fixed width layout for specific pages
@media (min-width: 768px) {
- settings-screen, home-page, page-dashboard-list, page-queries-list, page-alerts-list, alert-page, queries-search-results-page, .fixed-container {
+ .settings-screen, home-page, page-dashboard-list, page-queries-list, page-alerts-list, alert-page, queries-search-results-page, .fixed-container {
.container {
width: 750px;
}
@@ -93,7 +93,7 @@ strong {
}
@media (min-width: 992px) {
- settings-screen, home-page, page-dashboard-list, page-queries-list, page-alerts-list, alert-page, queries-search-results-page, .fixed-container {
+ .settings-screen, home-page, page-dashboard-list, page-queries-list, page-alerts-list, alert-page, queries-search-results-page, .fixed-container {
.container {
width: 970px;
}
@@ -101,7 +101,7 @@ strong {
}
@media (min-width: 1200px) {
- settings-screen, home-page, page-dashboard-list, page-queries-list, page-alerts-list, alert-page, queries-search-results-page, .fixed-container {
+ .settings-screen, home-page, page-dashboard-list, page-queries-list, page-alerts-list, alert-page, queries-search-results-page, .fixed-container {
.container {
width: 1170px;
}
diff --git a/client/app/components/SettingsWrapper.jsx b/client/app/components/SettingsWrapper.jsx
new file mode 100644
index 000000000..592dd6fff
--- /dev/null
+++ b/client/app/components/SettingsWrapper.jsx
@@ -0,0 +1,37 @@
+import React from 'react';
+import Menu from 'antd/lib/menu';
+import { PageHeader } from '@/components/PageHeader';
+import { $location } from '@/services/ng';
+import settingsMenu from '@/services/settingsMenu';
+
+
+function wrapSettingsTab(options, WrappedComponent) {
+ if (options) {
+ settingsMenu.add(options);
+ }
+
+ return function SettingsTab(props) {
+ const activeItem = settingsMenu.getActiveItem($location.path());
+ return (
+
+
+
+
+
+
+
+
+
+ );
+ };
+}
+
+export default wrapSettingsTab;
diff --git a/client/app/components/cards-list/CardsList.less b/client/app/components/cards-list/CardsList.less
index ef9fbb1ab..809581126 100644
--- a/client/app/components/cards-list/CardsList.less
+++ b/client/app/components/cards-list/CardsList.less
@@ -2,6 +2,7 @@
@import '../../assets/less/inc/variables';
.visual-card-list {
+ width: 100%;
margin: -5px 0 0 -5px; // compensate for .visual-card spacing
}
diff --git a/client/app/components/settings-screen.html b/client/app/components/settings-screen.html
deleted file mode 100644
index d3a3c0a9e..000000000
--- a/client/app/components/settings-screen.html
+++ /dev/null
@@ -1,21 +0,0 @@
-
diff --git a/client/app/components/settings-screen.js b/client/app/components/settings-screen.js
deleted file mode 100644
index 80b168157..000000000
--- a/client/app/components/settings-screen.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import settingsMenu from '@/services/settingsMenu';
-import template from './settings-screen.html';
-
-export default function init(ngModule) {
- ngModule.component('settingsScreen', {
- transclude: true,
- template,
- controller($location, currentUser) {
- this.settingsMenu = settingsMenu;
- this.isActive = menuItem => menuItem.isActive($location.path());
- this.isAvailable = permission => permission === undefined || currentUser.hasPermission(permission);
- },
- });
-}
-
-init.init = true;
diff --git a/client/app/pages/data-sources/DataSourcesList.jsx b/client/app/pages/data-sources/DataSourcesList.jsx
index 3666ebd77..fd9f8bfad 100644
--- a/client/app/pages/data-sources/DataSourcesList.jsx
+++ b/client/app/pages/data-sources/DataSourcesList.jsx
@@ -2,7 +2,6 @@ import React from 'react';
import Button from 'antd/lib/button';
import { react2angular } from 'react2angular';
import { isEmpty, get } from 'lodash';
-import settingsMenu from '@/services/settingsMenu';
import { DataSource, IMG_ROOT } from '@/services/data-source';
import { policy } from '@/services/policy';
import navigateTo from '@/services/navigateTo';
@@ -13,6 +12,7 @@ import LoadingState from '@/components/items-list/components/LoadingState';
import CreateSourceDialog from '@/components/CreateSourceDialog';
import DynamicComponent from '@/components/DynamicComponent';
import helper from '@/components/dynamic-form/dynamicFormHelper';
+import wrapSettingsTab from '@/components/SettingsWrapper';
import recordEvent from '@/services/recordEvent';
class DataSourcesList extends React.Component {
@@ -115,14 +115,12 @@ class DataSourcesList extends React.Component {
}
export default function init(ngModule) {
- settingsMenu.add({
+ ngModule.component('pageDataSourcesList', react2angular(wrapSettingsTab({
permission: 'admin',
title: 'Data Sources',
path: 'data_sources',
order: 1,
- });
-
- ngModule.component('pageDataSourcesList', react2angular(DataSourcesList));
+ }, DataSourcesList)));
return routesToAngularRoutes([
{
@@ -137,7 +135,7 @@ export default function init(ngModule) {
isNewDataSourcePage: true,
},
], {
- template: '',
+ template: '',
controller($scope, $exceptionHandler) {
'ngInject';
diff --git a/client/app/pages/data-sources/EditDataSource.jsx b/client/app/pages/data-sources/EditDataSource.jsx
index 32910e110..65f0d39aa 100644
--- a/client/app/pages/data-sources/EditDataSource.jsx
+++ b/client/app/pages/data-sources/EditDataSource.jsx
@@ -12,6 +12,7 @@ import LoadingState from '@/components/items-list/components/LoadingState';
import DynamicForm from '@/components/dynamic-form/DynamicForm';
import helper from '@/components/dynamic-form/dynamicFormHelper';
import HelpTrigger, { TYPES as HELP_TRIGGER_TYPES } from '@/components/HelpTrigger';
+import wrapSettingsTab from '@/components/SettingsWrapper';
class EditDataSource extends React.Component {
static propTypes = {
@@ -134,11 +135,11 @@ class EditDataSource extends React.Component {
}
export default function init(ngModule) {
- ngModule.component('pageEditDataSource', react2angular(EditDataSource));
+ ngModule.component('pageEditDataSource', react2angular(wrapSettingsTab(null, EditDataSource)));
return {
'/data_sources/:dataSourceId': {
- template: '',
+ template: '',
title: 'Data Sources',
controller($scope, $exceptionHandler) {
'ngInject';
diff --git a/client/app/pages/destinations/DestinationsList.jsx b/client/app/pages/destinations/DestinationsList.jsx
index 2f68efddf..3a95bb81c 100644
--- a/client/app/pages/destinations/DestinationsList.jsx
+++ b/client/app/pages/destinations/DestinationsList.jsx
@@ -2,7 +2,6 @@ import React from 'react';
import Button from 'antd/lib/button';
import { react2angular } from 'react2angular';
import { isEmpty, get } from 'lodash';
-import settingsMenu from '@/services/settingsMenu';
import { Destination, IMG_ROOT } from '@/services/destination';
import { policy } from '@/services/policy';
import navigateTo from '@/services/navigateTo';
@@ -12,6 +11,7 @@ import CardsList from '@/components/cards-list/CardsList';
import LoadingState from '@/components/items-list/components/LoadingState';
import CreateSourceDialog from '@/components/CreateSourceDialog';
import helper from '@/components/dynamic-form/dynamicFormHelper';
+import wrapSettingsTab from '@/components/SettingsWrapper';
class DestinationsList extends React.Component {
state = {
@@ -110,14 +110,12 @@ class DestinationsList extends React.Component {
}
export default function init(ngModule) {
- settingsMenu.add({
+ ngModule.component('pageDestinationsList', react2angular(wrapSettingsTab({
permission: 'admin',
title: 'Alert Destinations',
path: 'destinations',
order: 4,
- });
-
- ngModule.component('pageDestinationsList', react2angular(DestinationsList));
+ }, DestinationsList)));
return routesToAngularRoutes([
{
@@ -132,7 +130,7 @@ export default function init(ngModule) {
isNewDestinationPage: true,
},
], {
- template: '',
+ template: '',
controller($scope, $exceptionHandler) {
'ngInject';
diff --git a/client/app/pages/destinations/EditDestination.jsx b/client/app/pages/destinations/EditDestination.jsx
index 91ec92f26..180c73648 100644
--- a/client/app/pages/destinations/EditDestination.jsx
+++ b/client/app/pages/destinations/EditDestination.jsx
@@ -11,6 +11,7 @@ import PromiseRejectionError from '@/lib/promise-rejection-error';
import LoadingState from '@/components/items-list/components/LoadingState';
import DynamicForm from '@/components/dynamic-form/DynamicForm';
import helper from '@/components/dynamic-form/dynamicFormHelper';
+import wrapSettingsTab from '@/components/SettingsWrapper';
class EditDestination extends React.Component {
static propTypes = {
@@ -109,11 +110,11 @@ class EditDestination extends React.Component {
}
export default function init(ngModule) {
- ngModule.component('pageEditDestination', react2angular(EditDestination));
+ ngModule.component('pageEditDestination', react2angular(wrapSettingsTab(null, EditDestination)));
return {
'/destinations/:destinationId': {
- template: '',
+ template: '',
title: 'Alert Destinations',
controller($scope, $exceptionHandler) {
'ngInject';
diff --git a/client/app/pages/groups/GroupDataSources.jsx b/client/app/pages/groups/GroupDataSources.jsx
index 8570dedbb..f50888750 100644
--- a/client/app/pages/groups/GroupDataSources.jsx
+++ b/client/app/pages/groups/GroupDataSources.jsx
@@ -21,6 +21,7 @@ import GroupName from '@/components/groups/GroupName';
import ListItemAddon from '@/components/groups/ListItemAddon';
import Sidebar from '@/components/groups/DetailsPageSidebar';
import Layout from '@/components/layouts/ContentWithSidebar';
+import wrapSettingsTab from '@/components/SettingsWrapper';
import notification from '@/services/notification';
import { currentUser } from '@/services/auth';
@@ -222,7 +223,7 @@ class GroupDataSources extends React.Component {
}
export default function init(ngModule) {
- ngModule.component('pageGroupDataSources', react2angular(liveItemsList(
+ ngModule.component('pageGroupDataSources', react2angular(wrapSettingsTab(null, liveItemsList(
GroupDataSources,
new ResourceItemsSource({
isPlainList: true,
@@ -237,7 +238,7 @@ export default function init(ngModule) {
},
}),
new StateStorage({ orderByField: 'name' }),
- )));
+ ))));
return routesToAngularRoutes([
{
@@ -247,7 +248,7 @@ export default function init(ngModule) {
},
], {
reloadOnSearch: false,
- template: '',
+ template: '',
controller($scope, $exceptionHandler) {
'ngInject';
diff --git a/client/app/pages/groups/GroupMembers.jsx b/client/app/pages/groups/GroupMembers.jsx
index e08f158f1..d78ec46e7 100644
--- a/client/app/pages/groups/GroupMembers.jsx
+++ b/client/app/pages/groups/GroupMembers.jsx
@@ -18,6 +18,7 @@ import GroupName from '@/components/groups/GroupName';
import ListItemAddon from '@/components/groups/ListItemAddon';
import Sidebar from '@/components/groups/DetailsPageSidebar';
import Layout from '@/components/layouts/ContentWithSidebar';
+import wrapSettingsTab from '@/components/SettingsWrapper';
import notification from '@/services/notification';
import { currentUser } from '@/services/auth';
@@ -187,7 +188,7 @@ class GroupMembers extends React.Component {
}
export default function init(ngModule) {
- ngModule.component('pageGroupMembers', react2angular(liveItemsList(
+ ngModule.component('pageGroupMembers', react2angular(wrapSettingsTab(null, liveItemsList(
GroupMembers,
new ResourceItemsSource({
isPlainList: true,
@@ -202,7 +203,7 @@ export default function init(ngModule) {
},
}),
new StateStorage({ orderByField: 'name' }),
- )));
+ ))));
return routesToAngularRoutes([
{
@@ -212,7 +213,7 @@ export default function init(ngModule) {
},
], {
reloadOnSearch: false,
- template: '',
+ template: '',
controller($scope, $exceptionHandler) {
'ngInject';
diff --git a/client/app/pages/groups/GroupsList.jsx b/client/app/pages/groups/GroupsList.jsx
index 276414adf..179ef87d0 100644
--- a/client/app/pages/groups/GroupsList.jsx
+++ b/client/app/pages/groups/GroupsList.jsx
@@ -14,9 +14,9 @@ import ItemsTable, { Columns } from '@/components/items-list/components/ItemsTab
import CreateGroupDialog from '@/components/groups/CreateGroupDialog';
import DeleteGroupButton from '@/components/groups/DeleteGroupButton';
+import wrapSettingsTab from '@/components/SettingsWrapper';
import { Group } from '@/services/group';
-import settingsMenu from '@/services/settingsMenu';
import { currentUser } from '@/services/auth';
import navigateTo from '@/services/navigateTo';
import { routesToAngularRoutes } from '@/lib/utils';
@@ -121,14 +121,12 @@ class GroupsList extends React.Component {
}
export default function init(ngModule) {
- settingsMenu.add({
+ ngModule.component('pageGroupsList', react2angular(wrapSettingsTab({
permission: 'list_users',
title: 'Groups',
path: 'groups',
order: 3,
- });
-
- ngModule.component('pageGroupsList', react2angular(liveItemsList(
+ }, liveItemsList(
GroupsList,
new ResourceItemsSource({
isPlainList: true,
@@ -143,7 +141,7 @@ export default function init(ngModule) {
},
}),
new StateStorage({ orderByField: 'name', itemsPerPage: 10 }),
- )));
+ ))));
return routesToAngularRoutes([
{
@@ -153,7 +151,7 @@ export default function init(ngModule) {
},
], {
reloadOnSearch: false,
- template: '',
+ template: '',
controller($scope, $exceptionHandler) {
'ngInject';
diff --git a/client/app/pages/query-snippets/QuerySnippetsList.jsx b/client/app/pages/query-snippets/QuerySnippetsList.jsx
index 63de53122..71bbc2ec9 100644
--- a/client/app/pages/query-snippets/QuerySnippetsList.jsx
+++ b/client/app/pages/query-snippets/QuerySnippetsList.jsx
@@ -14,10 +14,10 @@ import { StateStorage } from '@/components/items-list/classes/StateStorage';
import LoadingState from '@/components/items-list/components/LoadingState';
import ItemsTable, { Columns } from '@/components/items-list/components/ItemsTable';
+import wrapSettingsTab from '@/components/SettingsWrapper';
import { QuerySnippet } from '@/services/query-snippet';
import navigateTo from '@/services/navigateTo';
-import settingsMenu from '@/services/settingsMenu';
import { currentUser } from '@/services/auth';
import { policy } from '@/services/policy';
import notification from '@/services/notification';
@@ -183,14 +183,12 @@ class QuerySnippetsList extends React.Component {
}
export default function init(ngModule) {
- settingsMenu.add({
+ ngModule.component('pageQuerySnippetsList', react2angular(wrapSettingsTab({
permission: 'create_query',
title: 'Query Snippets',
path: 'query_snippets',
order: 5,
- });
-
- ngModule.component('pageQuerySnippetsList', react2angular(liveItemsList(
+ }, liveItemsList(
QuerySnippetsList,
new ResourceItemsSource({
isPlainList: true,
@@ -205,7 +203,7 @@ export default function init(ngModule) {
},
}),
new StateStorage({ orderByField: 'trigger', itemsPerPage: 10 }),
- )));
+ ))));
return routesToAngularRoutes([
{
@@ -221,7 +219,7 @@ export default function init(ngModule) {
},
], {
reloadOnSearch: false,
- template: '',
+ template: '',
controller($scope, $exceptionHandler) {
'ngInject';
diff --git a/client/app/pages/settings/OrganizationSettings.jsx b/client/app/pages/settings/OrganizationSettings.jsx
index cfb970008..1effcc92f 100644
--- a/client/app/pages/settings/OrganizationSettings.jsx
+++ b/client/app/pages/settings/OrganizationSettings.jsx
@@ -13,10 +13,10 @@ import LoadingState from '@/components/items-list/components/LoadingState';
import { routesToAngularRoutes } from '@/lib/utils';
import { clientConfig } from '@/services/auth';
-import settingsMenu from '@/services/settingsMenu';
import recordEvent from '@/services/recordEvent';
import OrgSettings from '@/services/organizationSettings';
import HelpTrigger from '@/components/HelpTrigger';
+import wrapSettingsTab from '@/components/SettingsWrapper';
import DynamicComponent from '@/components/DynamicComponent';
const Option = Select.Option;
@@ -255,14 +255,12 @@ class OrganizationSettings extends React.Component {
}
export default function init(ngModule) {
- settingsMenu.add({
+ ngModule.component('pageOrganizationSettings', react2angular(wrapSettingsTab({
permission: 'admin',
title: 'Settings',
path: 'settings/organization',
order: 6,
- });
-
- ngModule.component('pageOrganizationSettings', react2angular(OrganizationSettings));
+ }, OrganizationSettings)));
return routesToAngularRoutes([
{
@@ -272,7 +270,7 @@ export default function init(ngModule) {
},
], {
reloadOnSearch: false,
- template: '',
+ template: '',
controller($scope, $exceptionHandler) {
'ngInject';
diff --git a/client/app/pages/users/UserProfile.jsx b/client/app/pages/users/UserProfile.jsx
index 7298b899f..323f4c9c8 100644
--- a/client/app/pages/users/UserProfile.jsx
+++ b/client/app/pages/users/UserProfile.jsx
@@ -6,9 +6,9 @@ import EmailSettingsWarning from '@/components/EmailSettingsWarning';
import UserEdit from '@/components/users/UserEdit';
import UserShow from '@/components/users/UserShow';
import LoadingState from '@/components/items-list/components/LoadingState';
+import wrapSettingsTab from '@/components/SettingsWrapper';
import { User } from '@/services/user';
-import settingsMenu from '@/services/settingsMenu';
import { $route } from '@/services/ng';
import { currentUser } from '@/services/auth';
import PromiseRejectionError from '@/lib/promise-rejection-error';
@@ -57,13 +57,11 @@ class UserProfile extends React.Component {
}
export default function init(ngModule) {
- settingsMenu.add({
+ ngModule.component('pageUserProfile', react2angular(wrapSettingsTab({
title: 'Account',
path: 'users/me',
order: 7,
- });
-
- ngModule.component('pageUserProfile', react2angular(UserProfile));
+ }, UserProfile)));
}
init.init = true;
diff --git a/client/app/pages/users/UsersList.jsx b/client/app/pages/users/UsersList.jsx
index 111de07f9..3b969bf49 100644
--- a/client/app/pages/users/UsersList.jsx
+++ b/client/app/pages/users/UsersList.jsx
@@ -21,8 +21,8 @@ import ItemsTable, { Columns } from '@/components/items-list/components/ItemsTab
import Layout from '@/components/layouts/ContentWithSidebar';
import CreateUserDialog from '@/components/users/CreateUserDialog';
+import wrapSettingsTab from '@/components/SettingsWrapper';
-import settingsMenu from '@/services/settingsMenu';
import { currentUser } from '@/services/auth';
import { policy } from '@/services/policy';
import { User } from '@/services/user';
@@ -231,15 +231,13 @@ class UsersList extends React.Component {
}
export default function init(ngModule) {
- settingsMenu.add({
+ ngModule.component('pageUsersList', react2angular(wrapSettingsTab({
permission: 'list_users',
title: 'Users',
path: 'users',
isActive: path => path.startsWith('/users') && (path !== '/users/me'),
order: 2,
- });
-
- ngModule.component('pageUsersList', react2angular(itemsList(
+ }, itemsList(
UsersList,
new ResourceItemsSource({
getRequest(request, { params: { currentPage } }) {
@@ -265,7 +263,7 @@ export default function init(ngModule) {
},
}),
new UrlStateStorage({ orderByField: 'created_at', orderByReverse: true }),
- )));
+ ))));
}
init.init = true;
diff --git a/client/app/pages/users/index.js b/client/app/pages/users/index.js
index 07d713e77..997a02288 100644
--- a/client/app/pages/users/index.js
+++ b/client/app/pages/users/index.js
@@ -25,7 +25,7 @@ export default function init() {
key: 'disabled',
},
], {
- template: '',
+ template: '',
reloadOnSearch: false,
controller($scope, $exceptionHandler) {
'ngInject';
@@ -47,7 +47,7 @@ export default function init() {
},
], {
reloadOnSearch: false,
- template: '',
+ template: '',
controller($scope, $exceptionHandler) {
'ngInject';
diff --git a/client/app/services/settingsMenu.js b/client/app/services/settingsMenu.js
index 45ee63015..c706c78af 100644
--- a/client/app/services/settingsMenu.js
+++ b/client/app/services/settingsMenu.js
@@ -1,4 +1,4 @@
-import { isFunction, extend, omit, sortBy } from 'lodash';
+import { isFunction, extend, omit, sortBy, find } from 'lodash';
class SettingsMenuItem {
constructor(menuItem) {
@@ -26,6 +26,10 @@ class SettingsMenu {
this.items.push(new SettingsMenuItem(item));
this.items = sortBy(this.items, 'order');
}
+
+ getActiveItem(path) {
+ return find(this.items, item => item.isActive(path));
+ }
}
export default new SettingsMenu();