diff --git a/frontend/src/metabase/components/ChannelSetupMessage.jsx b/frontend/src/metabase/components/ChannelSetupMessage.jsx
index 136d229ce525..e089faf87c89 100644
--- a/frontend/src/metabase/components/ChannelSetupMessage.jsx
+++ b/frontend/src/metabase/components/ChannelSetupMessage.jsx
@@ -13,7 +13,7 @@ export default class ChannelSetupMessage extends Component {
};
static defaultProps = {
- channels: ["email", "Slack"],
+ channels: ["email"],
};
render() {
diff --git a/frontend/src/metabase/components/ChannelSetupModal.jsx b/frontend/src/metabase/components/ChannelSetupModal.jsx
index 667e97c2f9e3..a270a7ad79d5 100644
--- a/frontend/src/metabase/components/ChannelSetupModal.jsx
+++ b/frontend/src/metabase/components/ChannelSetupModal.jsx
@@ -16,7 +16,7 @@ export default class ChannelSetupModal extends Component {
};
static defaultProps = {
- channels: ["email", "Slack"],
+ channels: ["email"],
};
render() {
diff --git a/frontend/src/metabase/components/CollectionLanding.jsx b/frontend/src/metabase/components/CollectionLanding.jsx
index 7a732dbbfa8e..643edc5c634f 100644
--- a/frontend/src/metabase/components/CollectionLanding.jsx
+++ b/frontend/src/metabase/components/CollectionLanding.jsx
@@ -72,7 +72,7 @@ const DashboardEmptyState = () => (
const PulseEmptyState = () => (
}
/>
diff --git a/frontend/src/metabase/components/ItemTypeFilterBar.jsx b/frontend/src/metabase/components/ItemTypeFilterBar.jsx
index d3fb6527fdd0..df69cd7b9b60 100644
--- a/frontend/src/metabase/components/ItemTypeFilterBar.jsx
+++ b/frontend/src/metabase/components/ItemTypeFilterBar.jsx
@@ -23,6 +23,11 @@ export const FILTERS = [
name: t`Questions`,
filter: "card",
icon: "beaker",
+ },
+ {
+ name: t`Pulses`,
+ filter: "pulse",
+ icon: "pulse",
}
];
diff --git a/frontend/src/metabase/containers/Overworld.jsx b/frontend/src/metabase/containers/Overworld.jsx
index b68ac312b7d1..2e9db758697c 100644
--- a/frontend/src/metabase/containers/Overworld.jsx
+++ b/frontend/src/metabase/containers/Overworld.jsx
@@ -229,11 +229,11 @@ export class AdminPinMessage extends React.Component {
const SectionHeading = ({ children }) => (
-
{children}
-
+
);
diff --git a/frontend/src/metabase/nav/containers/Navbar.jsx b/frontend/src/metabase/nav/containers/Navbar.jsx
index 68982c809026..1e6d8cb55a35 100644
--- a/frontend/src/metabase/nav/containers/Navbar.jsx
+++ b/frontend/src/metabase/nav/containers/Navbar.jsx
@@ -29,11 +29,13 @@ import ProfileLink from "metabase/nav/components/ProfileLink.jsx";
import { getPath, getContext, getUser } from "../selectors";
import { entityListLoader } from "metabase/entities/containers/EntityListLoader";
+import {getMetadata} from "metabase/selectors/metadata";
const mapStateToProps = (state, props) => ({
path: getPath(state, props),
context: getContext(state, props),
user: getUser(state),
+ metadata: getMetadata(state)
});
const mapDispatchToProps = {
@@ -41,29 +43,29 @@ const mapDispatchToProps = {
};
const AdminNavItem = ({ name, path, currentPath }) => (
-
-
- {name}
-
-
+
+
+ {name}
+
+
);
const DefaultSearchColor = color(colors.brand)
- .lighten(0.07)
- .string();
+ .lighten(0.07)
+ .string();
const ActiveSearchColor = color(colors.brand)
- .lighten(0.1)
- .string();
+ .lighten(0.1)
+ .string();
const SearchWrapper = Flex.extend`
${width} background-color: ${props =>
- props.active ? ActiveSearchColor : DefaultSearchColor};
+ props.active ? ActiveSearchColor : DefaultSearchColor};
border-radius: 6px;
align-items: center;
color: white;
@@ -114,34 +116,34 @@ class SearchBar extends React.Component {
render() {
const { active, searchText } = this.state;
return (
- this.setState({ active: false })}
- >
- this.setState({ active: true })}
- active={active}
+ this.setState({ active: false })}
>
-
- this.setState({ active: true })}
- onChange={e => this.setState({ searchText: e.target.value })}
- onKeyPress={e => {
- if (e.key === "Enter" && (searchText || "").trim().length > 0) {
- this.props.onChangeLocation({
- pathname: "search",
- query: { q: searchText },
- });
- }
- }}
- />
-
-
+ this.setState({ active: true })}
+ active={active}
+ >
+
+ this.setState({ active: true })}
+ onChange={e => this.setState({ searchText: e.target.value })}
+ onKeyPress={e => {
+ if (e.key === "Enter" && (searchText || "").trim().length > 0) {
+ this.props.onChangeLocation({
+ pathname: "search",
+ query: { q: searchText },
+ });
+ }
+ }}
+ />
+
+
);
}
}
@@ -187,138 +189,149 @@ export default class Navbar extends Component {
renderAdminNav() {
return (
- // NOTE: DO NOT REMOVE `Nav` CLASS FOR NOW, USED BY MODALS, FULLSCREEN DASHBOARD, ETC
- // TODO: hide nav using state in redux instead?
-
-
-
-
- {t`Report Builder Admin`}
-
+ // NOTE: DO NOT REMOVE `Nav` CLASS FOR NOW, USED BY MODALS, FULLSCREEN DASHBOARD, ETC
+ // TODO: hide nav using state in redux instead?
+
+
+
+
+ {t`Report Builder Admin`}
+
-
+
-
-
- {this.renderModal()}
-
+
+
+ {this.renderModal()}
+
);
}
renderEmptyNav() {
return (
- // NOTE: DO NOT REMOVE `Nav` CLASS FOR NOW, USED BY MODALS, FULLSCREEN DASHBOARD, ETC
- // TODO: hide nav using state in redux instead?
-
-
- {this.renderModal()}
-
+ // NOTE: DO NOT REMOVE `Nav` CLASS FOR NOW, USED BY MODALS, FULLSCREEN DASHBOARD, ETC
+ // TODO: hide nav using state in redux instead?
+
+
+ {this.renderModal()}
+
);
}
renderMainNav() {
const hasDataAccess =
- this.props.databases && this.props.databases.length > 0;
+ this.props.databases && this.props.databases.length > 0;
+ const hasSQLPermission = db => db.native_permissions === "write";
+ const showSQLOption = this.props.databases &&
+ this.props.databases.filter(hasSQLPermission).length > 0;
+ const itemsNav = [
+ {
+ title: t`New dashboard`,
+ icon: `dashboard`,
+ action: () => this.setModal(MODAL_NEW_DASHBOARD),
+ event: `NavBar;New Dashboard Click;`,
+ }];
+ if (showSQLOption) {
+ itemsNav.push({
+ title: t`New pulse`,
+ icon: `pulse`,
+ link: Urls.newPulse(),
+ event: `NavBar;New Pulse Click;`,
+ });
+ }
return (
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+ {hasDataAccess && (
+
+ {t`Ask a question`}
+
+ )}
+
-
-
-
- {hasDataAccess && (
-
- {t`Ask a question`}
-
- )}
- this.setModal(MODAL_NEW_DASHBOARD),
- event: `NavBar;New Dashboard Click;`,
- },
- ]}
- />
-
+
+
+ {this.renderModal()}
- {this.renderModal()}
-
);
}
@@ -326,14 +339,14 @@ export default class Navbar extends Component {
const { modal } = this.state;
if (modal) {
return (
- this.setState({ modal: null })}>
- {modal === MODAL_NEW_DASHBOARD ? (
- this.setState({ modal: null })}
- />
- ) : null}
-
+ this.setState({ modal: null })}>
+ {modal === MODAL_NEW_DASHBOARD ? (
+ this.setState({ modal: null })}
+ />
+ ) : null}
+
);
} else {
return null;
diff --git a/frontend/src/metabase/pulse/components/PulseEditChannels.jsx b/frontend/src/metabase/pulse/components/PulseEditChannels.jsx
index 69f5caf18735..f70a5bda5cd0 100644
--- a/frontend/src/metabase/pulse/components/PulseEditChannels.jsx
+++ b/frontend/src/metabase/pulse/components/PulseEditChannels.jsx
@@ -256,39 +256,41 @@ export default class PulseEditChannels extends Component {
let { pulse, user } = this.props;
let channels = pulse.channels
.map((c, i) => [c, i])
- .filter(([c, i]) => c.enabled && c.channel_type === channelSpec.type)
+ .filter(([c, i]) => c.enabled && c.channel_type === channelSpec.type && c.channel_type !== 'slack')
.map(([channel, index]) =>
this.renderChannel(channel, index, channelSpec),
);
- return (
-
-
- {CHANNEL_ICONS[channelSpec.type] && (
-
- )}
-
{channelSpec.name}
- 0}
- onChange={this.toggleChannel.bind(this, channelSpec.type)}
- />
-
- {channels.length > 0 && channelSpec.configured ? (
-
- ) : channels.length > 0 && !channelSpec.configured ? (
-
-
{t`${
- channelSpec.name
- } needs to be set up by an administrator.`}
-
-
- ) : null}
-
- );
+ if (channelSpec.type === 'email') {
+ return (
+
+
+ {CHANNEL_ICONS[channelSpec.type] && (
+
+ )}
+
{channelSpec.name}
+ 0}
+ onChange={this.toggleChannel.bind(this, channelSpec.type)}
+ />
+
+ {channels.length > 0 && channelSpec.configured ? (
+
+ ) : channels.length > 0 && !channelSpec.configured ? (
+
+
{t`${
+ channelSpec.name
+ } needs to be set up by an administrator.`}
+
+
+ ) : null}
+
+ );
+ }
}
render() {
@@ -296,7 +298,6 @@ export default class PulseEditChannels extends Component {
// Default to show the default channels until full formInput is loaded
let channels = formInput.channels || {
email: { name: t`Email`, type: "email" },
- slack: { name: t`Slack`, type: "slack" },
};
return (