diff --git a/x-pack/plugins/watcher/common/constants/pagination.ts b/x-pack/plugins/watcher/common/constants/pagination.ts index 8ae9d769831c56..31df84ca60ec0d 100644 --- a/x-pack/plugins/watcher/common/constants/pagination.ts +++ b/x-pack/plugins/watcher/common/constants/pagination.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export const PAGINATION: { [key: string]: number } = { - PAGE_SIZE: 20, +export const PAGINATION: { initialPageSize: number; pageSizeOptions: number[] } = { + initialPageSize: 10, + pageSizeOptions: [10, 50, 100], }; diff --git a/x-pack/plugins/watcher/public/sections/watch_list/components/watch_list.tsx b/x-pack/plugins/watcher/public/sections/watch_list/components/watch_list.tsx index 1e227110c9d355..631f56b03e46cc 100644 --- a/x-pack/plugins/watcher/public/sections/watch_list/components/watch_list.tsx +++ b/x-pack/plugins/watcher/public/sections/watch_list/components/watch_list.tsx @@ -25,7 +25,7 @@ import { Moment } from 'moment'; import chrome from 'ui/chrome'; import { MANAGEMENT_BREADCRUMB } from 'ui/management'; -import { REFRESH_INTERVALS } from '../../../../common/constants'; +import { REFRESH_INTERVALS, PAGINATION } from '../../../../common/constants'; import { listBreadcrumb } from '../../../lib/breadcrumbs'; import { getPageErrorCode, PageError, DeleteWatchesModal, WatchStatus } from '../../../components'; import { loadWatches } from '../../../lib/api'; @@ -160,11 +160,6 @@ const WatchListUi = ({ intl }: { intl: InjectedIntl }) => { onSelectionChange: setSelection, }; - const pagination = { - initialPageSize: 10, - pageSizeOptions: [10, 50, 100], - }; - const searchConfig = { box: { incremental: true, @@ -277,7 +272,7 @@ const WatchListUi = ({ intl }: { intl: InjectedIntl }) => { itemId="id" columns={columns} search={searchConfig} - pagination={pagination} + pagination={PAGINATION} sorting={true} selection={selectionConfig} isSelectable={true} diff --git a/x-pack/plugins/watcher/public/sections/watch_status/components/watch_detail.tsx b/x-pack/plugins/watcher/public/sections/watch_status/components/watch_detail.tsx index 2874cdf8a83b10..a8734c50eea674 100644 --- a/x-pack/plugins/watcher/public/sections/watch_status/components/watch_detail.tsx +++ b/x-pack/plugins/watcher/public/sections/watch_status/components/watch_detail.tsx @@ -12,35 +12,59 @@ import { toastNotifications } from 'ui/notify'; import { EuiInMemoryTable, EuiSpacer, - EuiText, EuiTitle, EuiButtonEmpty, EuiToolTip, + EuiCallOut, + EuiFlyout, + EuiFlyoutHeader, + EuiFlyoutBody, } from '@elastic/eui'; import { loadWatchDetail, ackWatchAction } from '../../../lib/api'; import { getPageErrorCode, WatchStatus } from '../../../components'; +import { PAGINATION } from '../../../../common/constants'; -const WatchDetailUi = ({ watchId }: { watchId: string }) => { - const pagination = { - initialPageSize: 10, - pageSizeOptions: [10, 50, 100], - }; +interface ActionError { + code: string; + message: string; +} +interface ActionStatus { + id: string; + isAckable: boolean; + state: string; + errors: ActionError[]; +} +const WatchDetailUi = ({ watchId }: { watchId: string }) => { const { error, data: watchDetail, isLoading } = loadWatchDetail(watchId); - const [actionStatuses, setActionStatuses] = useState([]); + const [actionStatuses, setActionStatuses] = useState([]); const [isActionStatusLoading, setIsActionStatusLoading] = useState(false); + const [selectedErrorAction, setSelectedErrorAction] = useState(null); + + const actionErrors = watchDetail && watchDetail.watchErrors.actionErrors; + const hasActionErrors = actionErrors && Object.keys(actionErrors).length > 0; + useEffect( () => { if (watchDetail) { - setActionStatuses(watchDetail.watchStatus.actionStatuses); + const currentActionStatuses = watchDetail.watchStatus.actionStatuses; + const actionStatusesWithErrors = + currentActionStatuses && + currentActionStatuses.map((currentActionStatus: ActionStatus) => { + return { + ...currentActionStatus, + errors: actionErrors ? actionErrors[currentActionStatus.id] : [], + }; + }); + setActionStatuses(actionStatusesWithErrors); } }, [watchDetail] ); - const columns = [ + const baseColumns = [ { field: 'id', name: i18n.translate('xpack.watcher.sections.watchDetail.watchTable.actionHeader', { @@ -48,9 +72,6 @@ const WatchDetailUi = ({ watchId }: { watchId: string }) => { }), sortable: true, truncateText: true, - render: (action: string) => { - return {action}; - }, }, { field: 'state', @@ -61,59 +82,92 @@ const WatchDetailUi = ({ watchId }: { watchId: string }) => { truncateText: true, render: (state: string) => , }, - { - actions: [ - { - render: (action: any) => { - if (action.isAckable) { - return ( - - { - setIsActionStatusLoading(true); - try { - const watchStatus = await ackWatchAction(watchDetail.id, action.id); - setIsActionStatusLoading(false); - return setActionStatuses(watchStatus.actionStatuses); - } catch (e) { - setIsActionStatusLoading(false); - toastNotifications.addDanger( - i18n.translate( - 'xpack.watcher.sections.watchDetail.watchTable.ackActionErrorMessage', - { - defaultMessage: 'Error acknowledging action {actionId}', - values: { - actionId: action.id, - }, - } - ) - ); + ]; + + const errorColumn = { + field: 'errors', + name: i18n.translate('xpack.watcher.sections.watchDetail.watchTable.errorsHeader', { + defaultMessage: 'Errors', + }), + render: (errors: ActionError[], action: ActionStatus) => { + if (errors && errors.length > 0) { + return ( + setSelectedErrorAction(action.id)}> + {i18n.translate('xpack.watcher.sections.watchDetail.watchTable.errorsCellText', { + defaultMessage: '{total, number} {total, plural, one {error} other {errors}}', + values: { + total: errors.length, + }, + })} + + ); + } + return ; + }, + }; + + const actionColumn = { + actions: [ + { + available: (action: ActionStatus) => action.isAckable, + render: (action: ActionStatus) => { + return ( + + { + setIsActionStatusLoading(true); + try { + const watchStatus = await ackWatchAction(watchDetail.id, action.id); + const newActionStatusesWithErrors = watchStatus.actionStatuses.map( + (newActionStatus: ActionStatus) => { + return { + ...newActionStatus, + errors: actionErrors ? actionErrors[newActionStatus.id] : [], + }; } - }} - > - - - - ); - } - return ; - }, + ); + setIsActionStatusLoading(false); + return setActionStatuses(newActionStatusesWithErrors); + } catch (e) { + setIsActionStatusLoading(false); + toastNotifications.addDanger( + i18n.translate( + 'xpack.watcher.sections.watchDetail.watchTable.ackActionErrorMessage', + { + defaultMessage: 'Error acknowledging action {actionId}', + values: { + actionId: action.id, + }, + } + ) + ); + } + }} + > + + + + ); }, - ], - }, - ]; + }, + ], + }; + + const columns = hasActionErrors + ? [...baseColumns, errorColumn, actionColumn] + : [...baseColumns, actionColumn]; // Another part of the UI will surface the error. if (getPageErrorCode(error)) { @@ -122,6 +176,40 @@ const WatchDetailUi = ({ watchId }: { watchId: string }) => { return ( + {selectedErrorAction && ( + setSelectedErrorAction(null)} + > + + +

{selectedErrorAction}

+
+
+ + + {actionErrors[selectedErrorAction].length > 1 ? ( +
    + {actionErrors[selectedErrorAction].map( + (actionError: ActionError, errorIndex: number) => ( +
  • {actionError.message}
  • + ) + )} +
+ ) : ( +

{actionErrors[selectedErrorAction][0].message}

+ )} +
+
+
+ )}

{ items={actionStatuses} itemId="id" columns={columns} - pagination={pagination} + pagination={PAGINATION} sorting={true} loading={isLoading} message={ diff --git a/x-pack/plugins/watcher/public/sections/watch_status/components/watch_history.tsx b/x-pack/plugins/watcher/public/sections/watch_status/components/watch_history.tsx index 4a365b2ebb1e5e..c1adf17d37ccad 100644 --- a/x-pack/plugins/watcher/public/sections/watch_status/components/watch_history.tsx +++ b/x-pack/plugins/watcher/public/sections/watch_status/components/watch_history.tsx @@ -26,6 +26,7 @@ import { EuiTitle, } from '@elastic/eui'; +import { PAGINATION } from '../../../../common/constants'; import { goToWatchList } from '../../../lib/navigation'; import { getPageErrorCode, PageError, WatchStatus, DeleteWatchesModal } from '../../../components'; import { @@ -109,11 +110,6 @@ const WatchHistoryUi = ({ intl, watchId }: { intl: InjectedIntl; watchId: string return ; } - const pagination = { - initialPageSize: 10, - pageSizeOptions: [10, 50, 100], - }; - const columns = [ { field: 'startTime', @@ -347,7 +343,7 @@ const WatchHistoryUi = ({ intl, watchId }: { intl: InjectedIntl; watchId: string