diff --git a/client/app/assets/less/ant.less b/client/app/assets/less/ant.less
index 5eb913eee7..0b9020d0e8 100644
--- a/client/app/assets/less/ant.less
+++ b/client/app/assets/less/ant.less
@@ -47,6 +47,7 @@
@zindex-dropdown: 2050;
@zindex-picker: 2050;
@zindex-tooltip: 2060;
+@item-hover-bg: #e5f8ff;
.@{drawer-prefix-cls} {
&.help-drawer {
diff --git a/client/app/assets/less/inc/base.less b/client/app/assets/less/inc/base.less
index 749d6ef4e0..1fb33c7f2f 100755
--- a/client/app/assets/less/inc/base.less
+++ b/client/app/assets/less/inc/base.less
@@ -29,7 +29,7 @@ body {
padding-top: 10px;
}
- .nav.app-header, .navbar {
+ .app-header-wrapper {
display: none;
}
}
@@ -182,7 +182,7 @@ text.slicetext {
color: #111;
}
-.profile__image--navbar {
+.profile__image--sidebar {
border-radius: 100%;
margin-right: 3px;
margin-top: -2px;
diff --git a/client/app/assets/less/inc/navbar.less b/client/app/assets/less/inc/navbar.less
deleted file mode 100755
index 3b912e8dea..0000000000
--- a/client/app/assets/less/inc/navbar.less
+++ /dev/null
@@ -1,295 +0,0 @@
-a.navbar-brand {
- padding: 5px 5px 0px 0px;
-}
-
-.navbar .fa {
- font-size: 18px;
-}
-
-.navbar .collapse.in {
- background: #222;
-}
-
-a.navbar-brand img {
- height: 40px;
-}
-
-.avatar {
- margin-top: 5px;
- margin-bottom: 5px;
-}
-
-.avatar img {
- width: 40px;
- height: 40px;
-}
-
-#logout {
- color: white;
- position: relative;
- left: -9px;
- bottom: -11px;
-}
-
-.caret--nav {
- border-top: none;
-}
-
-.caret--nav:after {
- content: "";
- position: absolute;
- right: 5px;
- top: 9px;
- width: 13px;
- height: 13px;
- display: block;
- background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='11px' height='6px' viewBox='0 0 11 6' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3C!-- Generator: Sketch 42 %2836781%29 - http://www.bohemiancoding.com/sketch --%3E%3Ctitle%3EShape%3C/title%3E%3Cdesc%3ECreated with Sketch.%3C/desc%3E%3Cdefs%3E%3C/defs%3E%3Cg id='Page-1' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E%3Cpath d='M5.296,4.288 L9.382,0.2 C9.66086822,-0.0716916976 10.1065187,-0.068122925 10.381,0.208 C10.661,0.488 10.661,0.932 10.388,1.206 L5.792,5.803 C5.6602899,5.93388911 5.48167943,6.00662966 5.296,6.005 C5.10997499,6.00689786 4.93095449,5.93413702 4.799,5.803 L0.204,1.207 C0.072163111,1.07394937 -0.00121750401,0.893846387 9.62313189e-05,0.706545264 C0.00140996665,0.519244142 0.0773097323,0.340188219 0.211,0.209 C0.485365732,-0.0664648737 0.930253538,-0.0700311086 1.209,0.201 L5.296,4.288 L5.296,4.288 Z' id='Shape' fill='%23000000'%3E%3C/path%3E%3C/g%3E%3C/svg%3E");
- background-size: 100% 100%;
- transition: transform .2s cubic-bezier(.75,0,.25,1);
-}
-
-.navbar .caret--nav:after {
- top: 19px;
-}
-
-.dropdown--profile .caret--nav:after {
- right: 8px;
-}
-
-.btn--create {
- padding-right: 20px;
-
- .caret--nav:after {
- top: 10px;
- right: 10px;
- background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='11px' height='6px' viewBox='0 0 11 6' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3C!-- Generator: Sketch 42 %2836781%29 - http://www.bohemiancoding.com/sketch --%3E%3Ctitle%3EShape%3C/title%3E%3Cdesc%3ECreated with Sketch.%3C/desc%3E%3Cdefs%3E%3C/defs%3E%3Cg id='Page-1' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E%3Cpath d='M5.29592111,4.28945339 L9.38192111,0.201453387 C9.66078932,-0.0702383105 10.1064398,-0.0666695379 10.3809211,0.209453387 C10.6609211,0.489453387 10.6609211,0.933453387 10.3879211,1.20745339 L5.79192111,5.80445339 C5.66021101,5.9353425 5.48160054,6.00808305 5.29592111,6.00645339 C5.1098961,6.00835125 4.9308756,5.9355904 4.79892111,5.80445339 L0.203921109,1.20845339 C0.0720842204,1.07540275 -0.00129639464,0.895299774 1.73406884e-05,0.707998651 C0.00133107602,0.520697529 0.0772308417,0.341641606 0.210921109,0.210453387 C0.485286842,-0.0650114866 0.930174648,-0.0685777215 1.20892111,0.202453387 L5.29592111,4.28945339 L5.29592111,4.28945339 Z' id='Shape' fill='%23FCFCFC'%3E%3C/path%3E%3C/g%3E%3C/svg%3E");
- }
-}
-
-.dropdown.open .caret--nav:after {
- transform: rotate(180deg);
-}
-
-.navbar {
- box-shadow: fade(@redash-gray, 15%) 0px 4px 9px -3px;
-
- .navbar-collapse {
- padding-left: 0;
- }
-
- a.dropdown--profile {
- padding-top: 10px;
- padding-bottom: 10px;
- line-height: 2.35;
- }
-
- .navbar-inverse {
- background-color: @redash-gray;
- border: none;
- }
-}
-
-.navbar-btn {
- margin-top: 10px;
- margin-bottom: 9px;
-}
-
-.navbar-brand {
- position: absolute;
- left: 50%;
- margin-left: -25px !important; // center
- display: block;
- zoom: 0.9;
-}
-
-.menu-search {
- margin-top: 2px;
-}
-
-.dropdown-menu--profile {
- li {
- width: 200px;
- }
-}
-
-.navbar .collapse.in {
- background: #fff;
- position: relative;
- z-index: 999;
- padding: 0 10px 0 10px;
-}
-.navbar {
- min-height: initial;
- height: 50px;
- border: 1px solid #fff;
- border-top: none;
- border-radius: 0;
- background: #fff;
- margin-bottom: 10px;
-
- .btn-group.open .dropdown-toggle {
- -webkit-box-shadow: none;
- box-shadow: none;
- }
-
- .btn-group .btn:active {
- box-shadow: none;
- }
-}
-
-.navbar-link-ANGULAR_REMOVE_ME {
- line-height: 18px;
- padding: 10px 15px;
- display: block;
-
- @media (min-width: 768px) {
- padding-top: 16px;
- padding-bottom: 16px;
- }
-}
-
-.navbar-link-ANGULAR_REMOVE_ME,
-.navbar-default .navbar-nav > li > a {
- color: #000;
- font-weight: 500;
-
- &:active, &:hover, &:focus {
- color: #000;
- }
-}
-
-.navbar-default .btn__new button {
- font-weight: 500;
-}
-
-.btn__new {
- margin-left: 15px;
-}
-
-.navbar-default .navbar-nav > li > a:hover {
- //background-color: fade(@redash-gray, 10%);
- //text-decoration: underline;
- //border-radius: 0;
-}
-
-.navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .open > a:hover, .navbar-default .navbar-nav > .open > a:focus {
- background-color: fade(@redash-gray, 15%);
- color: #111;
-}
-
-
-// Responsive fixes
-@media (max-width: 767px) {
- .navbar-brand {
- left: 2%;
- margin-left: 0 !important;
- }
-
- //Fix navbar collapse
- .navbar .collapse.in {
- border: none;
-
- .dropdown-menu--profile {
- li {
- width: auto;
- }
- }
-
- .dropdown--profile {
- .caret--nav:after {
- right: initial !important;
- }
- }
-
- .dropdown--profile__username {
- display: inline-block;
- }
-
- .nav__main li a {
- padding: 10px 15px;
- display: block;
- text-align: left;
- float: none !important;
- }
-
- .navbar-form {
- margin-bottom: 0;
- margin-top: 0;
- }
-
- .navbar-right {
- margin-bottom: 0;
- }
- }
-}
-
-@media (min-width: 768px) {
- @media (max-width: 880px) {
- .navbar-link-ANGULAR_REMOVE_ME,
- .navbar-default .navbar-nav > li > a,
- .navbar-form {
- padding-left: 10px !important;
- padding-right: 10px !important;
- }
-
- a.navbar-brand {
- margin-left: -15px !important;
- }
- }
-
- @media (max-width: 810px) {
- .menu-search {
- width: 175px;
- }
-
- a.navbar-brand {
- margin-left: 13px !important;
- }
- }
-}
-
-@media (max-width: 1084px) {
- .dropdown--profile__username {
- display: none;
- }
-}
-
-
-
-
-// Cross-browser fixes
-
-// Firefox
-@-moz-document url-prefix() {
- .caret--nav::after {
- height: 7px;
- }
-
- .navbar .caret--nav::after {
- top: 22px;
- }
-
- .navbar .btn--create .caret--nav::after {
- top: 12px;
- }
-}
-
-// IE10+
-@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
- .caret--nav::after {
- height: 7px;
- }
-
- .navbar .caret--nav::after {
- top: 22px;
- }
-
- .navbar .btn--create .caret--nav::after {
- top: 12px;
- }
-}
-
-
-.navbar li a .btn-favourite .fa, .navbar li a .btn-archive .fa {
- font-size: 100%;
-}
\ No newline at end of file
diff --git a/client/app/assets/less/main.less b/client/app/assets/less/main.less
index 0f764f8144..2e4cc000e9 100644
--- a/client/app/assets/less/main.less
+++ b/client/app/assets/less/main.less
@@ -44,7 +44,6 @@
@import 'inc/profile';
@import 'inc/404';
@import 'inc/ie-warning';
-@import 'inc/navbar';
@import 'inc/edit-in-place';
@import 'inc/growl';
@import 'inc/flex';
diff --git a/client/app/components/BeaconConsent.jsx b/client/app/components/BeaconConsent.jsx
index e551eb6a0f..665ffe0c30 100644
--- a/client/app/components/BeaconConsent.jsx
+++ b/client/app/components/BeaconConsent.jsx
@@ -4,7 +4,7 @@ import Card from 'antd/lib/card';
import Button from 'antd/lib/button';
import Typography from 'antd/lib/typography';
import { clientConfig } from '@/services/auth';
-import { HelpTrigger } from '@/components/HelpTrigger';
+import HelpTrigger from '@/components/HelpTrigger';
import DynamicComponent from '@/components/DynamicComponent';
import OrgSettings from '@/services/organizationSettings';
diff --git a/client/app/components/CreateSourceDialog.jsx b/client/app/components/CreateSourceDialog.jsx
index 3f7c2eda5e..cc5818601e 100644
--- a/client/app/components/CreateSourceDialog.jsx
+++ b/client/app/components/CreateSourceDialog.jsx
@@ -11,7 +11,7 @@ import { PreviewCard } from '@/components/PreviewCard';
import EmptyState from '@/components/items-list/components/EmptyState';
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 HelpTrigger, { TYPES as HELP_TRIGGER_TYPES } from '@/components/HelpTrigger';
const { Step } = Steps;
const { Search } = Input;
diff --git a/client/app/components/EmailSettingsWarning.jsx b/client/app/components/EmailSettingsWarning.jsx
index 999052ee74..eabe0ee04d 100644
--- a/client/app/components/EmailSettingsWarning.jsx
+++ b/client/app/components/EmailSettingsWarning.jsx
@@ -4,7 +4,7 @@ import cx from 'classnames';
import { clientConfig, currentUser } from '@/services/auth';
import Tooltip from 'antd/lib/tooltip';
import Alert from 'antd/lib/alert';
-import { HelpTrigger } from '@/components/HelpTrigger';
+import HelpTrigger from '@/components/HelpTrigger';
export default function EmailSettingsWarning({ featureName, className, mode, adminOnly }) {
if (!clientConfig.mailSettingsMissing) {
diff --git a/client/app/components/HelpTrigger.jsx b/client/app/components/HelpTrigger.jsx
index ae57b250da..75b2a6f8dd 100644
--- a/client/app/components/HelpTrigger.jsx
+++ b/client/app/components/HelpTrigger.jsx
@@ -1,4 +1,3 @@
-import { react2angular } from 'react2angular';
import React from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
@@ -80,9 +79,13 @@ export const TYPES = {
'/user-guide/alerts/custom-alert-notifications',
'Guide: Custom Alerts Notifications',
],
+ FAVORITES: [
+ '/user-guide/querying/favorites-tagging/#Favorites',
+ 'Guide: Favorites',
+ ],
};
-export class HelpTrigger extends React.Component {
+export default class HelpTrigger extends React.Component {
static propTypes = {
type: PropTypes.oneOf(Object.keys(TYPES)).isRequired,
className: PropTypes.string,
@@ -236,9 +239,3 @@ export class HelpTrigger extends React.Component {
);
}
}
-
-export default function init(ngModule) {
- ngModule.component('helpTrigger', react2angular(HelpTrigger));
-}
-
-init.init = true;
diff --git a/client/app/components/ParameterMappingInput.jsx b/client/app/components/ParameterMappingInput.jsx
index bb138f2b8e..05c2f80925 100644
--- a/client/app/components/ParameterMappingInput.jsx
+++ b/client/app/components/ParameterMappingInput.jsx
@@ -18,7 +18,7 @@ import Tooltip from 'antd/lib/tooltip';
import ParameterValueInput from '@/components/ParameterValueInput';
import { ParameterMappingType } from '@/services/widget';
import { Parameter } from '@/services/query';
-import { HelpTrigger } from '@/components/HelpTrigger';
+import HelpTrigger from '@/components/HelpTrigger';
import './ParameterMappingInput.less';
diff --git a/client/app/components/app-header/AppHeader.jsx b/client/app/components/app-header/AppHeader.jsx
new file mode 100644
index 0000000000..69a4f68045
--- /dev/null
+++ b/client/app/components/app-header/AppHeader.jsx
@@ -0,0 +1,257 @@
+/* eslint-disable no-template-curly-in-string */
+
+import React, { useRef } from 'react';
+import { react2angular } from 'react2angular';
+
+import Dropdown from 'antd/lib/dropdown';
+import Button from 'antd/lib/button';
+import Icon from 'antd/lib/icon';
+import Menu from 'antd/lib/menu';
+import Input from 'antd/lib/input';
+import Tooltip from 'antd/lib/tooltip';
+
+import FavoritesDropdown from './components/FavoritesDropdown';
+import HelpTrigger from '@/components/HelpTrigger';
+import CreateDashboardDialog from '@/components/dashboards/CreateDashboardDialog';
+
+import { currentUser, Auth, clientConfig } from '@/services/auth';
+import { $location, $route } from '@/services/ng';
+import { Dashboard } from '@/services/dashboard';
+import { Query } from '@/services/query';
+import frontendVersion from '@/version.json';
+import logoUrl from '@/assets/images/redash_icon_small.png';
+
+import './AppHeader.less';
+
+function onSearch(q) {
+ $location.path('/queries').search({ q });
+ $route.reload();
+}
+
+function DesktopNavbar() {
+ return (
+
+
+
+
+ {currentUser.hasPermission('create_query') && (
+
+ New Query
+
+ )}
+ {currentUser.hasPermission('create_dashboard') && (
+
+ New Dashboard
+
+ )}
+
+ New Alert
+
+
+ )}
+ >
+
+
+
+
+
+
+
+ )}
+ >
+
+
+
+
+
+
+ );
+}
+
+function MobileNavbar() {
+ const ref = useRef();
+
+ return (
+
+
+
+
ref.current} // so the overlay menu stays with the fixed header when page scrolls
+ overlay={(
+
+ )}
+ >
+
+
+
+
+ );
+}
+
+export function AppHeader() {
+ return (
+
+ );
+}
+
+export default function init(ngModule) {
+ ngModule.component('appHeader', react2angular(AppHeader));
+}
+
+init.init = true;
diff --git a/client/app/components/app-header/AppHeader.less b/client/app/components/app-header/AppHeader.less
new file mode 100644
index 0000000000..9a0ec84762
--- /dev/null
+++ b/client/app/components/app-header/AppHeader.less
@@ -0,0 +1,207 @@
+@mobileBreakpoint: ~"(max-width: 767px)";
+
+nav .app-header {
+ height: 49px;
+ padding-bottom: 1px;
+ box-sizing: content-box;
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 10px;
+ background: white;
+ box-shadow: 0 4px 9px -3px rgba(102, 136, 153, .15);
+
+ .darker {
+ color: #333 !important;
+
+ &:hover {
+ color: #2196F3 !important;
+ }
+ }
+
+ & > * {
+ display: flex;
+ align-items: center;
+ }
+
+ &[data-platform="mobile"] {
+ display: none;
+ }
+
+ .menu-item-button {
+ padding: 0 15px;
+ font-size: 18px;
+ .darker();
+ }
+
+ .ant-menu-root {
+ margin: 0 10px;
+ line-height: 50px;
+ height: 50px;
+ border-bottom: 0;
+ }
+
+ .ant-btn {
+ font-weight: 500;
+
+ .anticon {
+ margin-right: 0;
+ }
+ }
+
+ &[data-platform="desktop"] .ant-btn:not(.ant-btn-primary) {
+ border: 0;
+ box-shadow: none;
+ height: 40px;
+ line-height: 40px;
+ background-color: transparent; //so it doesn't interfere with click animation of adjacent buttons
+ .darker();
+ }
+
+ .ant-menu-item {
+ padding: 0;
+ height: 52px;
+ display: inline-flex;
+ align-items: center;
+
+ .anticon-down {
+ font-size: 13px !important;
+ transform: none;
+ position: relative;
+ top: 2px;
+
+ svg {
+ transition: transform .2s cubic-bezier(.75,0,.25,1);
+ }
+ }
+
+ .ant-dropdown-open .anticon-down svg,
+ .anticon-down.ant-dropdown-open svg {
+ transform: rotate(180deg);
+ }
+ }
+
+ .dropdown-menu-item {
+ .ant-btn {
+ padding-right: 5px;
+ padding-left: 5px;
+ margin-right: 30px;
+ margin-left: 10px;
+ position: relative;
+ z-index: 1;
+ }
+
+ // this is a trick to get the dropdown menu to be placed at the bottom left
+ // of the menu item and not the dropdown trigger
+ .ant-dropdown-trigger {
+ position: absolute;
+ top: 5px;
+ right: 0;
+ left: 10px;
+ bottom: 5px;
+ text-align: right;
+ padding-top: 14px;
+ padding-right: 10px;
+ margin-right: 0;
+ user-select: none; // or else double clicking it causes the header logo to get selected
+ .darker();
+ }
+ }
+
+ .header-logo img {
+ height: 40px;
+ width: 40px;
+ }
+
+ .searchbar {
+ width: 185px;
+ }
+
+ .profile-dropdown {
+ display: flex;
+ align-items: center;
+
+ span {
+ max-width: 130px; // arbitrary, prevents layout mess up if username long
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+ }
+
+ img {
+ height: 20px;
+ width: 20px;
+ border-radius: 50%;
+ margin-right: 5px;
+ }
+ }
+
+ @media (max-width: 960px) {
+ .ant-btn,
+ .menu-item-button {
+ padding: 0 10px;
+ }
+
+ .ant-menu-root {
+ margin: 0 5px;
+ }
+
+ .profile-dropdown {
+ span {
+ display: none;
+ }
+
+ img {
+ margin-right: 0;
+ }
+ }
+ }
+
+ @media (max-width: 800px) {
+ .searchbar {
+ width: 140px;
+ }
+ }
+
+ @media @mobileBreakpoint {
+ &[data-platform="desktop"] {
+ display: none;
+ }
+
+ &[data-platform="mobile"] {
+ display: flex;
+ padding: 0 15px;
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ box-sizing: border-box;
+ z-index: 1000;
+ }
+ }
+}
+
+@media @mobileBreakpoint {
+ .app-header-wrapper {
+ margin-top: 59px !important; // compensate for app header fixed position
+ }
+}
+
+.update-available {
+ display: inline !important;
+
+ .fa {
+ color: #52c41a;
+ vertical-align: text-bottom;
+ font-size: 16px;
+ }
+}
+
+.ant-dropdown-menu-item .help-trigger {
+ display: inline;
+ color: #2196F3;
+ vertical-align: bottom;
+}
+
+.ant-dropdown-menu.favorites-dropdown {
+ margin-left: -10px;
+}
\ No newline at end of file
diff --git a/client/app/components/app-header/app-header.css b/client/app/components/app-header/app-header.css
deleted file mode 100644
index bf50033bcd..0000000000
--- a/client/app/components/app-header/app-header.css
+++ /dev/null
@@ -1,13 +0,0 @@
-.menu-search input[type="text"] {
- height: 30px;
-}
-
-.dropdown-menu__version {
- padding: 5px 10px 8px 17px;
-}
-
-.update-available .fa {
- color: #52c41a;
- vertical-align: bottom;
- font-size: 16px !important;
-}
\ No newline at end of file
diff --git a/client/app/components/app-header/app-header.html b/client/app/components/app-header/app-header.html
deleted file mode 100644
index 64d442f369..0000000000
--- a/client/app/components/app-header/app-header.html
+++ /dev/null
@@ -1,234 +0,0 @@
-
diff --git a/client/app/components/app-header/components/FavoritesDropdown.jsx b/client/app/components/app-header/components/FavoritesDropdown.jsx
new file mode 100644
index 0000000000..5155decde7
--- /dev/null
+++ b/client/app/components/app-header/components/FavoritesDropdown.jsx
@@ -0,0 +1,69 @@
+import React, { useState, useMemo, useCallback, useEffect } from 'react';
+import PropTypes from 'prop-types';
+import { isEmpty, template } from 'lodash';
+
+import Dropdown from 'antd/lib/dropdown';
+import Icon from 'antd/lib/icon';
+import Menu from 'antd/lib/menu';
+
+import HelpTrigger from '@/components/HelpTrigger';
+
+export default function FavoritesDropdown({ fetch, urlTemplate }) {
+ const [items, setItems] = useState();
+ const [loading, setLoading] = useState(false);
+
+ const noItems = isEmpty(items);
+ const urlCompiled = useMemo(() => template(urlTemplate), [urlTemplate]);
+
+ const fetchItems = useCallback(() => {
+ setLoading(true);
+ fetch().$promise
+ .then(({ results }) => {
+ setItems(results);
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+ }, [fetch]);
+
+ // fetch items on init
+ useEffect(fetchItems, []);
+
+ // fetch items on click
+ const onVisibleChange = visible => visible && fetchItems();
+
+ const menu = (
+
+ );
+
+ return (
+
+ {loading ? : }
+
+ );
+}
+
+FavoritesDropdown.propTypes = {
+ fetch: PropTypes.func.isRequired,
+ urlTemplate: PropTypes.string.isRequired,
+};
diff --git a/client/app/components/app-header/index.js b/client/app/components/app-header/index.js
deleted file mode 100644
index d29fdca658..0000000000
--- a/client/app/components/app-header/index.js
+++ /dev/null
@@ -1,58 +0,0 @@
-import debug from 'debug';
-import CreateDashboardDialog from '@/components/dashboards/CreateDashboardDialog';
-
-import logoUrl from '@/assets/images/redash_icon_small.png';
-import frontendVersion from '@/version.json';
-import template from './app-header.html';
-import './app-header.css';
-
-const logger = debug('redash:appHeader');
-
-function controller($rootScope, $location, $route, $uibModal, Auth, currentUser, clientConfig, Dashboard, Query) {
- this.logoUrl = logoUrl;
- this.basePath = clientConfig.basePath;
- this.currentUser = currentUser;
- this.showQueriesMenu = currentUser.hasPermission('view_query');
- this.showAlertsLink = currentUser.hasPermission('list_alerts');
- this.showNewQueryMenu = currentUser.hasPermission('create_query');
- this.showSettingsMenu = currentUser.hasPermission('list_users');
- this.showDashboardsMenu = currentUser.hasPermission('list_dashboards');
-
- this.frontendVersion = frontendVersion;
- this.backendVersion = clientConfig.version;
- this.newVersionAvailable = clientConfig.newVersionAvailable && currentUser.isAdmin;
-
- this.reload = () => {
- logger('Reloading dashboards and queries.');
- Dashboard.favorites().$promise.then((data) => {
- this.dashboards = data.results;
- });
- Query.favorites().$promise.then((data) => {
- this.queries = data.results;
- });
- };
-
- this.reload();
-
- $rootScope.$on('reloadFavorites', this.reload);
-
- this.newDashboard = () => CreateDashboardDialog.showModal();
-
- this.searchQueries = () => {
- $location.path('/queries').search({ q: this.searchTerm });
- $route.reload();
- };
-
- this.logout = () => {
- Auth.logout();
- };
-}
-
-export default function init(ngModule) {
- ngModule.component('appHeader', {
- template,
- controller,
- });
-}
-
-init.init = true;
diff --git a/client/app/components/items-list/components/Sidebar.jsx b/client/app/components/items-list/components/Sidebar.jsx
index 89684a2274..c850c28adb 100644
--- a/client/app/components/items-list/components/Sidebar.jsx
+++ b/client/app/components/items-list/components/Sidebar.jsx
@@ -105,7 +105,7 @@ export function ProfileImage({ user }) {
if (!isString(user.profile_image_url) || (user.profile_image_url === '')) {
return null;
}
- return ;
+ return ;
}
ProfileImage.propTypes = {
diff --git a/client/app/pages/alert/AlertEdit.jsx b/client/app/pages/alert/AlertEdit.jsx
index 0caa1cc0f9..b3a38c9947 100644
--- a/client/app/pages/alert/AlertEdit.jsx
+++ b/client/app/pages/alert/AlertEdit.jsx
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { HelpTrigger } from '@/components/HelpTrigger';
+import HelpTrigger from '@/components/HelpTrigger';
import { Alert as AlertType } from '@/components/proptypes';
import Form from 'antd/lib/form';
diff --git a/client/app/pages/alert/AlertNew.jsx b/client/app/pages/alert/AlertNew.jsx
index 26b18f9f66..f192e43821 100644
--- a/client/app/pages/alert/AlertNew.jsx
+++ b/client/app/pages/alert/AlertNew.jsx
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { HelpTrigger } from '@/components/HelpTrigger';
+import HelpTrigger from '@/components/HelpTrigger';
import { Alert as AlertType } from '@/components/proptypes';
import Form from 'antd/lib/form';
diff --git a/client/app/pages/alert/components/NotificationTemplate.jsx b/client/app/pages/alert/components/NotificationTemplate.jsx
index 31e10bdc12..4e500c0f4f 100644
--- a/client/app/pages/alert/components/NotificationTemplate.jsx
+++ b/client/app/pages/alert/components/NotificationTemplate.jsx
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { head, isEmpty, isNull, isUndefined } from 'lodash';
import Mustache from 'mustache';
-import { HelpTrigger } from '@/components/HelpTrigger';
+import HelpTrigger from '@/components/HelpTrigger';
import { Alert as AlertType, Query as QueryType } from '@/components/proptypes';
import Input from 'antd/lib/input';
diff --git a/client/app/pages/dashboards/ShareDashboardDialog.jsx b/client/app/pages/dashboards/ShareDashboardDialog.jsx
index be555af857..0b131c2667 100644
--- a/client/app/pages/dashboards/ShareDashboardDialog.jsx
+++ b/client/app/pages/dashboards/ShareDashboardDialog.jsx
@@ -9,7 +9,7 @@ import { $http } from '@/services/ng';
import notification from '@/services/notification';
import { wrap as wrapDialog, DialogPropType } from '@/components/DialogWrapper';
import InputWithCopy from '@/components/InputWithCopy';
-import { HelpTrigger } from '@/components/HelpTrigger';
+import HelpTrigger from '@/components/HelpTrigger';
const API_SHARE_URL = 'api/dashboards/{id}/share';
diff --git a/client/app/pages/data-sources/EditDataSource.jsx b/client/app/pages/data-sources/EditDataSource.jsx
index 4db6045387..32910e110a 100644
--- a/client/app/pages/data-sources/EditDataSource.jsx
+++ b/client/app/pages/data-sources/EditDataSource.jsx
@@ -11,7 +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 { HelpTrigger, TYPES as HELP_TRIGGER_TYPES } from '@/components/HelpTrigger';
+import HelpTrigger, { TYPES as HELP_TRIGGER_TYPES } from '@/components/HelpTrigger';
class EditDataSource extends React.Component {
static propTypes = {
diff --git a/client/app/pages/settings/OrganizationSettings.jsx b/client/app/pages/settings/OrganizationSettings.jsx
index 32e4d6c83b..cfb9700086 100644
--- a/client/app/pages/settings/OrganizationSettings.jsx
+++ b/client/app/pages/settings/OrganizationSettings.jsx
@@ -16,7 +16,7 @@ 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 HelpTrigger from '@/components/HelpTrigger';
import DynamicComponent from '@/components/DynamicComponent';
const Option = Select.Option;
diff --git a/client/cypress/integration/dashboard/dashboard_spec.js b/client/cypress/integration/dashboard/dashboard_spec.js
index 42876c1b13..d9f7f8d297 100644
--- a/client/cypress/integration/dashboard/dashboard_spec.js
+++ b/client/cypress/integration/dashboard/dashboard_spec.js
@@ -12,7 +12,7 @@ describe('Dashboard', () => {
cy.visit('/dashboards');
cy.getByTestId('CreateButton').click();
cy.get('li[role="menuitem"]')
- .contains('Dashboard')
+ .contains('New Dashboard')
.click();
cy.server();