diff --git a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplate.jsx b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplate.jsx
new file mode 100644
index 0000000000..d271962a40
--- /dev/null
+++ b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplate.jsx
@@ -0,0 +1,5 @@
+import React from 'react';
+
+export default function NotificationTemplate() {
+ return
;
+}
diff --git a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateAdd.jsx b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateAdd.jsx
new file mode 100644
index 0000000000..bbf39b61a9
--- /dev/null
+++ b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateAdd.jsx
@@ -0,0 +1,5 @@
+import React from 'react';
+
+export default function NotificationTemplateAdd() {
+ return ;
+}
diff --git a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateList.jsx b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateList.jsx
new file mode 100644
index 0000000000..50d0f3f400
--- /dev/null
+++ b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateList.jsx
@@ -0,0 +1,170 @@
+import React, { useCallback, useEffect } from 'react';
+import { useLocation, useRouteMatch } from 'react-router-dom';
+import { withI18n } from '@lingui/react';
+import { t } from '@lingui/macro';
+import { Card, PageSection } from '@patternfly/react-core';
+import { NotificationTemplatesAPI } from '../../../api';
+import PaginatedDataList, {
+ ToolbarAddButton,
+ ToolbarDeleteButton,
+} from '../../../components/PaginatedDataList';
+import AlertModal from '../../../components/AlertModal';
+import ErrorDetail from '../../../components/ErrorDetail';
+import DataListToolbar from '../../../components/DataListToolbar';
+import NotificationTemplateListItem from './NotificationTemplateListItem';
+import useRequest, { useDeleteItems } from '../../../util/useRequest';
+import useSelected from '../../../util/useSelected';
+import { getQSConfig, parseQueryString } from '../../../util/qs';
+
+const QS_CONFIG = getQSConfig('notification-templates', {
+ page: 1,
+ page_size: 20,
+ order_by: 'name',
+});
+
+function NotificationTemplatesList({ i18n }) {
+ const location = useLocation();
+ const match = useRouteMatch();
+
+ const addUrl = `${match.url}/add`;
+
+ const {
+ result: { templates, count, actions },
+ error: contentError,
+ isLoading: isTemplatesLoading,
+ request: fetchTemplates,
+ } = useRequest(
+ useCallback(async () => {
+ const params = parseQueryString(QS_CONFIG, location.search);
+ const responses = await Promise.all([
+ NotificationTemplatesAPI.read(params),
+ NotificationTemplatesAPI.readOptions(),
+ ]);
+ return {
+ templates: responses[0].data.results,
+ count: responses[0].data.count,
+ actions: responses[1].data.actions,
+ };
+ }, [location]),
+ {
+ templates: [],
+ count: 0,
+ actions: {},
+ }
+ );
+
+ useEffect(() => {
+ fetchTemplates();
+ }, [fetchTemplates]);
+
+ const { selected, isAllSelected, handleSelect, setSelected } = useSelected(
+ templates
+ );
+
+ const {
+ isLoading: isDeleteLoading,
+ deleteItems: deleteTemplates,
+ deletionError,
+ clearDeletionError,
+ } = useDeleteItems(
+ useCallback(async () => {
+ return Promise.all(
+ selected.map(({ id }) => NotificationTemplatesAPI.destroy(id))
+ );
+ }, [selected]),
+ {
+ qsConfig: QS_CONFIG,
+ allItemsSelected: isAllSelected,
+ fetchItems: fetchTemplates,
+ }
+ );
+
+ const handleDelete = async () => {
+ await deleteTemplates();
+ setSelected([]);
+ };
+
+ const canAdd = actions && actions.POST;
+
+ return (
+ <>
+
+
+ (
+ setSelected([...templates])}
+ qsConfig={QS_CONFIG}
+ additionalControls={[
+ ...(canAdd
+ ? []
+ : []),
+ ,
+ ]}
+ />
+ )}
+ renderItem={template => (
+ row.id === template.id)}
+ onSelect={() => handleSelect(template)}
+ />
+ )}
+ emptyStateControls={
+ canAdd ? : null
+ }
+ />
+
+
+
+ {i18n._(t`Failed to delete one or more organizations.`)}
+
+
+ >
+ );
+}
+
+export default withI18n()(NotificationTemplatesList);
diff --git a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateListItem.jsx b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateListItem.jsx
new file mode 100644
index 0000000000..4bf773b4f8
--- /dev/null
+++ b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateListItem.jsx
@@ -0,0 +1,45 @@
+import React from 'react';
+import { withI18n } from '@lingui/react';
+import { t } from '@lingui/macro';
+import { Link } from 'react-router-dom';
+import {
+ Badge as PFBadge,
+ Button,
+ DataListAction as _DataListAction,
+ DataListCheck,
+ DataListItem,
+ DataListItemCells,
+ DataListItemRow,
+ Tooltip,
+} from '@patternfly/react-core';
+import DataListCell from '../../../components/DataListCell';
+
+export default function NotificationTemplatesListItem({
+ template,
+ detailUrl,
+ isSelected,
+ onSelect,
+}) {
+ const labelId = `check-action-${template.id}`;
+ return (
+
+
+
+
+
+ {template.name}
+
+ ,
+ ]}
+ />
+
+
+ );
+}
diff --git a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/index.js b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/index.js
new file mode 100644
index 0000000000..06c347d889
--- /dev/null
+++ b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/index.js
@@ -0,0 +1,4 @@
+import NotificationTemplatesList from './NotificationTemplateList';
+
+export default NotificationTemplatesList;
+export { default as NotificationTemplatesListItem } from './NotificationTemplateListItem';
diff --git a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplates.jsx b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplates.jsx
index 857201bc6b..6d828175a0 100644
--- a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplates.jsx
+++ b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplates.jsx
@@ -1,28 +1,48 @@
-import React, { Component, Fragment } from 'react';
+import React, { useState } from 'react';
+import { Route, Switch, useRouteMatch } from 'react-router-dom';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
-import {
- PageSection,
- PageSectionVariants,
- Title,
-} from '@patternfly/react-core';
+import NotificationTemplateList from './NotificationTemplateList';
+import NotificationTemplateAdd from './NotificationTemplateAdd';
+import NotificationTemplate from './NotificationTemplate';
+import Breadcrumbs from '../../components/Breadcrumbs/Breadcrumbs';
-class NotificationTemplates extends Component {
- render() {
- const { i18n } = this.props;
- const { light } = PageSectionVariants;
+function NotificationTemplates({ i18n }) {
+ const match = useRouteMatch();
+ const [breadcrumbConfig, setBreadcrumbConfig] = useState({
+ '/notification_templates': i18n._(t`Notification Templates`),
+ '/notification_templates/add': i18n._(t`Create New Notification Template`),
+ });
- return (
-
-
-
- {i18n._(t`Notification Templates`)}
-
-
-
-
- );
- }
+ const updateBreadcrumbConfig = notification => {
+ const { id } = notification;
+ setBreadcrumbConfig({
+ '/notification_templates': i18n._(t`Notification Templates`),
+ '/notification_templates/add': i18n._(
+ t`Create New Notification Template`
+ ),
+ [`/notification_templates/${id}`]: notification.name,
+ [`/notification_templates/${id}/edit`]: i18n._(t`Edit Details`),
+ [`/notification_templates/${id}/details`]: i18n._(t`Details`),
+ });
+ };
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
}
export default withI18n()(NotificationTemplates);