mirror of
https://github.com/getredash/redash.git
synced 2025-12-19 17:37:19 -05:00
219 lines
6.6 KiB
JavaScript
219 lines
6.6 KiB
JavaScript
import { without, find, includes, map, toLower } from "lodash";
|
|
import React from "react";
|
|
import PropTypes from "prop-types";
|
|
|
|
import SelectItemsDialog from "@/components/SelectItemsDialog";
|
|
import { Destination as DestinationType, UserProfile as UserType } from "@/components/proptypes";
|
|
|
|
import DestinationService, { IMG_ROOT } from "@/services/destination";
|
|
import AlertSubscription from "@/services/alert-subscription";
|
|
import { clientConfig, currentUser } from "@/services/auth";
|
|
import notification from "@/services/notification";
|
|
import ListItemAddon from "@/components/groups/ListItemAddon";
|
|
import EmailSettingsWarning from "@/components/EmailSettingsWarning";
|
|
|
|
import CloseOutlinedIcon from "@ant-design/icons/CloseOutlined";
|
|
import Tooltip from "antd/lib/tooltip";
|
|
import Switch from "antd/lib/switch";
|
|
import Button from "antd/lib/button";
|
|
|
|
import "./AlertDestinations.less";
|
|
|
|
const USER_EMAIL_DEST_ID = -1;
|
|
|
|
function normalizeSub(sub) {
|
|
if (!sub.destination) {
|
|
sub.destination = {
|
|
id: USER_EMAIL_DEST_ID,
|
|
name: sub.user.email,
|
|
icon: "DEPRECATED",
|
|
type: "email",
|
|
};
|
|
}
|
|
return sub;
|
|
}
|
|
|
|
function ListItem({ destination: { name, type }, user, unsubscribe }) {
|
|
const canUnsubscribe = currentUser.isAdmin || currentUser.id === user.id;
|
|
|
|
return (
|
|
<li className="destination-wrapper">
|
|
<img src={`${IMG_ROOT}/${type}.png`} className="destination-icon" alt={name} />
|
|
<span className="flex-fill">{name}</span>
|
|
{type === "email" && (
|
|
<EmailSettingsWarning className="destination-warning" featureName="alert emails" mode="icon" />
|
|
)}
|
|
{canUnsubscribe && (
|
|
<Tooltip title="Remove" mouseEnterDelay={0.5}>
|
|
<CloseOutlinedIcon className="remove-button" onClick={unsubscribe} />
|
|
</Tooltip>
|
|
)}
|
|
</li>
|
|
);
|
|
}
|
|
|
|
ListItem.propTypes = {
|
|
destination: DestinationType.isRequired,
|
|
user: UserType.isRequired,
|
|
unsubscribe: PropTypes.func.isRequired,
|
|
};
|
|
|
|
export default class AlertDestinations extends React.Component {
|
|
static propTypes = {
|
|
alertId: PropTypes.any.isRequired,
|
|
};
|
|
|
|
state = {
|
|
dests: [],
|
|
subs: null,
|
|
};
|
|
|
|
componentDidMount() {
|
|
const { alertId } = this.props;
|
|
Promise.all([
|
|
DestinationService.query(), // get all destinations
|
|
AlertSubscription.query({ alertId }), // get subcriptions per alert
|
|
]).then(([dests, subs]) => {
|
|
subs = subs.map(normalizeSub);
|
|
this.setState({ dests, subs });
|
|
});
|
|
}
|
|
|
|
showAddAlertSubDialog = () => {
|
|
const { dests, subs } = this.state;
|
|
|
|
SelectItemsDialog.showModal({
|
|
width: 570,
|
|
showCount: true,
|
|
extraFooterContent: (
|
|
<>
|
|
<i className="fa fa-info-circle" /> Create new destinations in{" "}
|
|
<Tooltip title="Opens page in a new tab.">
|
|
<a href="destinations/new" target="_blank">
|
|
Alert Destinations
|
|
</a>
|
|
</Tooltip>
|
|
</>
|
|
),
|
|
dialogTitle: "Add Existing Alert Destinations",
|
|
inputPlaceholder: "Search destinations...",
|
|
searchItems: searchTerm => {
|
|
searchTerm = toLower(searchTerm);
|
|
return Promise.resolve(dests.filter(d => includes(toLower(d.name), searchTerm)));
|
|
},
|
|
renderItem: (item, { isSelected }) => {
|
|
const alreadyInGroup = !!find(subs, s => s.destination.id === item.id);
|
|
|
|
return {
|
|
content: (
|
|
<div className="destination-wrapper">
|
|
<img src={`${IMG_ROOT}/${item.type}.png`} className="destination-icon" alt={item.name} />
|
|
<span className="flex-fill">{item.name}</span>
|
|
<ListItemAddon isSelected={isSelected} alreadyInGroup={alreadyInGroup} deselectedIcon="fa-plus" />
|
|
</div>
|
|
),
|
|
isDisabled: alreadyInGroup,
|
|
className: isSelected || alreadyInGroup ? "selected" : "",
|
|
};
|
|
},
|
|
}).onClose(items => {
|
|
const promises = map(items, item => this.subscribe(item));
|
|
return Promise.all(promises)
|
|
.then(() => {
|
|
notification.success("Subscribed.");
|
|
})
|
|
.catch(() => {
|
|
notification.error("Failed saving subscription.");
|
|
return Promise.reject(null); // keep dialog visible but suppress its default error message
|
|
});
|
|
});
|
|
};
|
|
|
|
onUserEmailToggle = sub => {
|
|
if (sub) {
|
|
this.unsubscribe(sub);
|
|
} else {
|
|
this.subscribe();
|
|
}
|
|
};
|
|
|
|
subscribe = dest => {
|
|
const { alertId } = this.props;
|
|
|
|
const sub = { alert_id: alertId };
|
|
if (dest) {
|
|
sub.destination_id = dest.id;
|
|
}
|
|
|
|
return AlertSubscription.create(sub).then(sub => {
|
|
const { subs } = this.state;
|
|
this.setState({
|
|
subs: [...subs, normalizeSub(sub)],
|
|
});
|
|
});
|
|
};
|
|
|
|
unsubscribe = sub => {
|
|
AlertSubscription.delete(sub)
|
|
.then(() => {
|
|
// not showing subscribe notification cause it's redundant here
|
|
const { subs } = this.state;
|
|
this.setState({
|
|
subs: without(subs, sub),
|
|
});
|
|
})
|
|
.catch(() => {
|
|
notification.error("Failed unsubscribing.");
|
|
});
|
|
};
|
|
|
|
render() {
|
|
if (!this.props.alertId) {
|
|
return null;
|
|
}
|
|
|
|
const { subs } = this.state;
|
|
const currentUserEmailSub = find(subs, {
|
|
destination: { id: USER_EMAIL_DEST_ID },
|
|
user: { id: currentUser.id },
|
|
});
|
|
const filteredSubs = without(subs, currentUserEmailSub);
|
|
const { mailSettingsMissing } = clientConfig;
|
|
|
|
return (
|
|
<div className="alert-destinations" data-test="AlertDestinations">
|
|
<Tooltip title='Click to add an existing "Alert Destination"' mouseEnterDelay={0.5}>
|
|
<Button
|
|
data-test="ShowAddAlertSubDialog"
|
|
type="primary"
|
|
size="small"
|
|
className="add-button"
|
|
onClick={this.showAddAlertSubDialog}>
|
|
<i className="fa fa-plus f-12 m-r-5" /> Add
|
|
</Button>
|
|
</Tooltip>
|
|
<ul>
|
|
<li className="destination-wrapper">
|
|
<i className="destination-icon fa fa-envelope" />
|
|
<span className="flex-fill">{currentUser.email}</span>
|
|
<EmailSettingsWarning className="destination-warning" featureName="alert emails" mode="icon" />
|
|
{!mailSettingsMissing && (
|
|
<Switch
|
|
size="small"
|
|
className="toggle-button"
|
|
checked={!!currentUserEmailSub}
|
|
loading={!subs}
|
|
onChange={() => this.onUserEmailToggle(currentUserEmailSub)}
|
|
data-test="UserEmailToggle"
|
|
/>
|
|
)}
|
|
</li>
|
|
{filteredSubs.map(s => (
|
|
<ListItem key={s.id} unsubscribe={() => this.unsubscribe(s)} {...s} />
|
|
))}
|
|
</ul>
|
|
</div>
|
|
);
|
|
}
|
|
}
|