more template details; add template delete button

This commit is contained in:
Keith Grant 2020-08-05 11:47:36 -07:00
parent ab4628b199
commit a9451c9864
8 changed files with 266 additions and 27 deletions

View File

@ -2,14 +2,27 @@ import React, { useEffect, useCallback } from 'react';
import { t } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { Card, PageSection } from '@patternfly/react-core';
import { Link, useParams } from 'react-router-dom';
import { CaretLeftIcon } from '@patternfly/react-icons';
import {
Link,
Switch,
Route,
Redirect,
useParams,
useRouteMatch,
useLocation,
} from 'react-router-dom';
import useRequest from '../../util/useRequest';
import RoutedTabs from '../../components/RoutedTabs';
import ContentError from '../../components/ContentError';
import { NotificationTemplatesAPI } from '../../api';
import NotificationTemplateDetail from './NotificationTemplateDetail';
import NotificationTemplateEdit from './NotificationTemplateEdit';
function NotificationTemplate({ i18n, setBreadcrumb }) {
function NotificationTemplate({ setBreadcrumb, i18n }) {
const { id: templateId } = useParams();
const match = useRouteMatch();
const location = useLocation();
const {
result: template,
isLoading,
@ -47,15 +60,51 @@ function NotificationTemplate({ i18n, setBreadcrumb }) {
);
}
const showCardHeader = !isLoading && !location.pathname.endsWith('edit');
const tabs = [
{
name: (
<>
<CaretLeftIcon />
{i18n._(t`Back to Notifications`)}
</>
),
link: `/notification_templates`,
id: 99,
},
{
name: i18n._(t`Details`),
link: `${match.url}/details`,
id: 0,
},
];
return (
<PageSection>
<Card>
{template && (
<NotificationTemplateDetail
template={template}
isLoading={isLoading}
{showCardHeader && <RoutedTabs tabsArray={tabs} />}
<Switch>
<Redirect
from="/notification_templates/:id"
to="/notification_templates/:id/details"
exact
/>
)}
{template && (
<>
<Route path="/notification_templates/:id/edit">
<NotificationTemplateEdit
template={template}
isLoading={isLoading}
/>
</Route>
<Route path="/notification_templates/:id/details">
<NotificationTemplateDetail
template={template}
isLoading={isLoading}
/>
</Route>
</>
)}
</Switch>
</Card>
</PageSection>
);

View File

@ -1,5 +1,5 @@
import React, { Fragment, useState, useEffect, useCallback } from 'react';
import { Link, useHistory, useParams } from 'react-router-dom';
import React, { useState, useEffect, useCallback } from 'react';
import { Link, useHistory } from 'react-router-dom';
import { withI18n } from '@lingui/react';
import {
Button,
@ -28,26 +28,27 @@ import DeleteButton from '../../../components/DeleteButton';
import ErrorDetail from '../../../components/ErrorDetail';
import LaunchButton from '../../../components/LaunchButton';
import { VariablesDetail } from '../../../components/CodeMirrorInput';
import { JobTemplatesAPI } from '../../../api';
import { NotificationTemplatesAPI } from '../../../api';
import useRequest, { useDismissableError } from '../../../util/useRequest';
const TYPES = {
email: 'Email',
grafana: 'Grafana',
irc: 'IRC',
mattermost: 'Mattermost',
pagerduty: 'Pagerduty',
rocketchat: 'Rocket.Chat',
slack: 'Slack',
twilio: 'Twilio',
webhook: 'Webhook',
};
import { NOTIFICATION_TYPES } from '../constants';
function NotificationTemplateDetail({ i18n, template }) {
const history = useHistory();
const {
notification_configuration: configuration,
summary_fields,
} = template;
const { request: deleteTemplate, isLoading, error: deleteError } = useRequest(
useCallback(async () => {
await NotificationTemplatesAPI.destroy(template.id);
history.push(`/notification_templates`);
}, [template.id, history])
);
const { error, dismissError } = useDismissableError(deleteError);
return (
<CardBody>
<DetailList gutter="sm">
@ -78,7 +79,8 @@ function NotificationTemplateDetail({ i18n, template }) {
<Detail
label={i18n._(t`Notification Type`)}
value={
TYPES[template.notification_type] || template.notification_type
NOTIFICATION_TYPES[template.notification_type] ||
template.notification_type
}
dataCy="nt-detail-type"
/>
@ -265,7 +267,107 @@ function NotificationTemplateDetail({ i18n, template }) {
/>
</>
)}
{template.notification_type === 'slack' && (
<>
<Detail
label={i18n._(t`Destination Channels`)}
value={configuration.channels} // array
dataCy="nt-detail-slack-channels"
/>
<Detail
label={i18n._(t`Notification Color`)}
value={configuration.hex_color}
dataCy="nt-detail-slack-color"
/>
</>
)}
{template.notification_type === 'twilio' && (
<>
<Detail
label={i18n._(t`Source Phone Number`)}
value={configuration.from_number}
dataCy="nt-detail-twilio-source-phone"
/>
<Detail
label={i18n._(t`Destination SMS Number`)}
value={configuration.to_numbers} // array
dataCy="nt-detail-twilio-destination-numbers"
/>
<Detail
label={i18n._(t`Account SID`)}
value={configuration.account_sid}
dataCy="nt-detail-twilio-account-sid"
/>
</>
)}
{template.notification_type === 'webhook' && (
<>
<Detail
label={i18n._(t`Username`)}
value={configuration.username}
dataCy="nt-detail-webhook-password"
/>
<Detail
label={i18n._(t`Target URL`)}
value={configuration.url}
dataCy="nt-detail-webhook-url"
/>
<Detail
label={i18n._(t`Disable SSL Verification`)}
value={
configuration.disable_ssl_verification
? i18n._(t`True`)
: i18n._(t`False`)
}
dataCy="nt-detail-disable-ssl"
/>
<Detail
label={i18n._(t`HTTP Method`)}
value={configuration.http_method}
dataCy="nt-detail-webhook-http-method"
/>
{/* <Detail
label={i18n._(t`HTTP Headers`)}
value={configuration.headers}
dataCy="nt-detail-webhook-headers"
/> */}
</>
)}
</DetailList>
<CardActionsRow>
{summary_fields.user_capabilities &&
summary_fields.user_capabilities.edit && (
<Button
component={Link}
to={`/notification_templates/${template.id}/edit`}
aria-label={i18n._(t`Edit`)}
>
{i18n._(t`Edit`)}
</Button>
)}
{summary_fields.user_capabilities &&
summary_fields.user_capabilities.delete && (
<DeleteButton
name={template.name}
modalTitle={i18n._(t`Delete Notification`)}
onConfirm={deleteTemplate}
isDisabled={isLoading}
>
{i18n._(t`Delete`)}
</DeleteButton>
)}
</CardActionsRow>
{error && (
<AlertModal
isOpen={error}
variant="error"
title={i18n._(t`Error!`)}
onClose={dismissError}
>
{i18n._(t`Failed to delete notification.`)}
<ErrorDetail error={error} />
</AlertModal>
)}
</CardBody>
);
}

View File

@ -0,0 +1,68 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { useHistory } from 'react-router-dom';
import { CardBody } from '../../../components/Card';
import { OrganizationsAPI } from '../../../api';
import { Config } from '../../../contexts/Config';
import NotificationTemplateForm from '../shared/NotificationTemplateForm';
function NotificationTemplateEdit({ template }) {
const detailsUrl = `/notification_templates/${template.id}/details`;
const history = useHistory();
const [formError, setFormError] = useState(null);
const handleSubmit = async (
values,
groupsToAssociate,
groupsToDisassociate
) => {
try {
await OrganizationsAPI.update(template.id, values);
await Promise.all(
groupsToAssociate.map(id =>
OrganizationsAPI.associateInstanceGroup(template.id, id)
)
);
await Promise.all(
groupsToDisassociate.map(id =>
OrganizationsAPI.disassociateInstanceGroup(template.id, id)
)
);
history.push(detailsUrl);
} catch (error) {
setFormError(error);
}
};
const handleCancel = () => {
history.push(detailsUrl);
};
return (
<CardBody>
<Config>
{({ me }) => (
<NotificationTemplateForm
template={template}
onSubmit={handleSubmit}
onCancel={handleCancel}
me={me || {}}
submitError={formError}
/>
)}
</Config>
</CardBody>
);
}
NotificationTemplateEdit.propTypes = {
template: PropTypes.shape().isRequired,
};
NotificationTemplateEdit.contextTypes = {
custom_virtualenvs: PropTypes.arrayOf(PropTypes.string),
};
export { NotificationTemplateEdit as _NotificationTemplateEdit };
export default NotificationTemplateEdit;

View File

@ -0,0 +1,3 @@
import NotificationTemplateEdit from './NotificationTemplateEdit';
export default NotificationTemplateEdit;

View File

@ -14,6 +14,7 @@ import {
} from '@patternfly/react-core';
import { PencilAltIcon, BellIcon } from '@patternfly/react-icons';
import DataListCell from '../../../components/DataListCell';
import { NOTIFICATION_TYPES } from '../constants';
const DataListAction = styled(_DataListAction)`
align-items: center;
@ -49,7 +50,8 @@ function NotificationTemplateListItem({
</Link>
</DataListCell>,
<DataListCell key="type">
{template.notification_type}
{NOTIFICATION_TYPES[template.notification_type] ||
template.notification_type}
</DataListCell>,
]}
/>

View File

@ -1,4 +1,4 @@
import NotificationTemplatesList from './NotificationTemplateList';
import NotificationTemplateList from './NotificationTemplateList';
export default NotificationTemplatesList;
export { default as NotificationTemplatesListItem } from './NotificationTemplateListItem';
export default NotificationTemplateList;
export { default as NotificationTemplateListItem } from './NotificationTemplateListItem';

View File

@ -0,0 +1,12 @@
/* eslint-disable-next-line import/prefer-default-export */
export const NOTIFICATION_TYPES = {
email: 'Email',
grafana: 'Grafana',
irc: 'IRC',
mattermost: 'Mattermost',
pagerduty: 'Pagerduty',
rocketchat: 'Rocket.Chat',
slack: 'Slack',
twilio: 'Twilio',
webhook: 'Webhook',
};

View File

@ -0,0 +1,3 @@
export default function NotificationTemplateForm() {
//
}