mirror of
https://github.com/ansible/awx.git
synced 2026-01-14 11:20:39 -03:30
JobOutput: extract JobOutputSearch bar
This commit is contained in:
parent
aefc28a0ed
commit
5473e54219
@ -12,39 +12,25 @@ import {
|
||||
import Ansi from 'ansi-to-html';
|
||||
import hasAnsi from 'has-ansi';
|
||||
import { encode } from 'html-entities';
|
||||
import {
|
||||
Button,
|
||||
Toolbar,
|
||||
ToolbarContent,
|
||||
ToolbarItem,
|
||||
ToolbarToggleGroup,
|
||||
Tooltip,
|
||||
} from '@patternfly/react-core';
|
||||
import { SearchIcon } from '@patternfly/react-icons';
|
||||
import { Button } from '@patternfly/react-core';
|
||||
|
||||
import AlertModal from 'components/AlertModal';
|
||||
import { CardBody as _CardBody } from 'components/Card';
|
||||
import ContentError from 'components/ContentError';
|
||||
import ContentLoading from 'components/ContentLoading';
|
||||
import ErrorDetail from 'components/ErrorDetail';
|
||||
import Search from 'components/Search';
|
||||
import StatusIcon from 'components/StatusIcon';
|
||||
|
||||
import { getJobModel, isJobRunning } from 'util/jobs';
|
||||
import useRequest, { useDismissableError } from 'hooks/useRequest';
|
||||
import useInterval from 'hooks/useInterval';
|
||||
import {
|
||||
parseQueryString,
|
||||
mergeParams,
|
||||
removeParams,
|
||||
getQSConfig,
|
||||
updateQueryString,
|
||||
} from 'util/qs';
|
||||
import { parseQueryString, getQSConfig } from 'util/qs';
|
||||
import useIsMounted from 'hooks/useIsMounted';
|
||||
import JobEvent from './JobEvent';
|
||||
import JobEventSkeleton from './JobEventSkeleton';
|
||||
import PageControls from './PageControls';
|
||||
import HostEventModal from './HostEventModal';
|
||||
import JobOutputSearch from './JobOutputSearch';
|
||||
import { HostStatusBar, OutputToolbar } from './shared';
|
||||
import getRowRangePageSize from './shared/jobOutputUtils';
|
||||
|
||||
@ -192,15 +178,6 @@ const OutputFooter = styled.div`
|
||||
flex: 1;
|
||||
`;
|
||||
|
||||
const SearchToolbar = styled(Toolbar)`
|
||||
position: inherit !important;
|
||||
`;
|
||||
|
||||
const SearchToolbarContent = styled(ToolbarContent)`
|
||||
padding-left: 0px !important;
|
||||
padding-right: 0px !important;
|
||||
`;
|
||||
|
||||
let ws;
|
||||
function connectJobSocket({ type, id }, onMessage) {
|
||||
ws = new WebSocket(
|
||||
@ -665,55 +642,6 @@ function JobOutput({ job, eventRelatedSearchableKeys, eventSearchableKeys }) {
|
||||
previousWidth.current = width;
|
||||
};
|
||||
|
||||
const handleSearch = (key, value) => {
|
||||
const params = parseQueryString(QS_CONFIG, location.search);
|
||||
const qs = updateQueryString(
|
||||
QS_CONFIG,
|
||||
location.search,
|
||||
mergeParams(params, { [key]: value })
|
||||
);
|
||||
pushHistoryState(qs);
|
||||
};
|
||||
|
||||
const handleReplaceSearch = (key, value) => {
|
||||
const qs = updateQueryString(QS_CONFIG, location.search, {
|
||||
[key]: value,
|
||||
});
|
||||
pushHistoryState(qs);
|
||||
};
|
||||
|
||||
const handleRemoveSearchTerm = (key, value) => {
|
||||
const oldParams = parseQueryString(QS_CONFIG, location.search);
|
||||
const updatedParams = removeParams(QS_CONFIG, oldParams, {
|
||||
[key]: value,
|
||||
});
|
||||
const qs = updateQueryString(QS_CONFIG, location.search, updatedParams);
|
||||
pushHistoryState(qs);
|
||||
};
|
||||
|
||||
const handleRemoveAllSearchTerms = () => {
|
||||
const oldParams = parseQueryString(QS_CONFIG, location.search);
|
||||
Object.keys(oldParams).forEach((key) => {
|
||||
oldParams[key] = null;
|
||||
});
|
||||
const qs = updateQueryString(QS_CONFIG, location.search, oldParams);
|
||||
pushHistoryState(qs);
|
||||
};
|
||||
|
||||
const pushHistoryState = (qs) => {
|
||||
const { pathname } = history.location;
|
||||
history.push(qs ? `${pathname}?${qs}` : pathname);
|
||||
};
|
||||
|
||||
const handleFollowToggle = () => {
|
||||
if (isFollowModeEnabled) {
|
||||
setIsFollowModeEnabled(false);
|
||||
} else {
|
||||
setIsFollowModeEnabled(true);
|
||||
scrollToRow(remoteRowCount - 1);
|
||||
}
|
||||
};
|
||||
|
||||
const handleScroll = (e) => {
|
||||
if (
|
||||
isFollowModeEnabled &&
|
||||
@ -726,64 +654,6 @@ function JobOutput({ job, eventRelatedSearchableKeys, eventSearchableKeys }) {
|
||||
scrollHeight.current = e.scrollHeight;
|
||||
};
|
||||
|
||||
const renderSearchComponent = () => (
|
||||
<Search
|
||||
qsConfig={QS_CONFIG}
|
||||
columns={[
|
||||
{
|
||||
name: t`Stdout`,
|
||||
key: 'stdout__icontains',
|
||||
isDefault: true,
|
||||
},
|
||||
{
|
||||
name: t`Event`,
|
||||
key: 'event',
|
||||
options: [
|
||||
['runner_on_failed', t`Host Failed`],
|
||||
['runner_on_start', t`Host Started`],
|
||||
['runner_on_ok', t`Host OK`],
|
||||
['runner_on_error', t`Host Failure`],
|
||||
['runner_on_skipped', t`Host Skipped`],
|
||||
['runner_on_unreachable', t`Host Unreachable`],
|
||||
['runner_on_no_hosts', t`No Hosts Remaining`],
|
||||
['runner_on_async_poll', t`Host Polling`],
|
||||
['runner_on_async_ok', t`Host Async OK`],
|
||||
['runner_on_async_failed', t`Host Async Failure`],
|
||||
['runner_item_on_ok', t`Item OK`],
|
||||
['runner_item_on_failed', t`Item Failed`],
|
||||
['runner_item_on_skipped', t`Item Skipped`],
|
||||
['runner_retry', t`Host Retry`],
|
||||
['runner_on_file_diff', t`File Difference`],
|
||||
['playbook_on_start', t`Playbook Started`],
|
||||
['playbook_on_notify', t`Running Handlers`],
|
||||
['playbook_on_include', t`Including File`],
|
||||
['playbook_on_no_hosts_matched', t`No Hosts Matched`],
|
||||
['playbook_on_no_hosts_remaining', t`No Hosts Remaining`],
|
||||
['playbook_on_task_start', t`Task Started`],
|
||||
['playbook_on_vars_prompt', t`Variables Prompted`],
|
||||
['playbook_on_setup', t`Gathering Facts`],
|
||||
['playbook_on_play_start', t`Play Started`],
|
||||
['playbook_on_stats', t`Playbook Complete`],
|
||||
['debug', t`Debug`],
|
||||
['verbose', t`Verbose`],
|
||||
['deprecated', t`Deprecated`],
|
||||
['warning', t`Warning`],
|
||||
['system_warning', t`System Warning`],
|
||||
['error', t`Error`],
|
||||
],
|
||||
},
|
||||
{ name: t`Advanced`, key: 'advanced' },
|
||||
]}
|
||||
searchableKeys={eventSearchableKeys}
|
||||
relatedSearchableKeys={eventRelatedSearchableKeys}
|
||||
onSearch={handleSearch}
|
||||
onReplaceSearch={handleReplaceSearch}
|
||||
onShowAdvancedSearch={() => {}}
|
||||
onRemove={handleRemoveSearchTerm}
|
||||
isDisabled={isJobRunning(job.status)}
|
||||
/>
|
||||
);
|
||||
|
||||
if (contentError) {
|
||||
return <ContentError error={contentError} />;
|
||||
}
|
||||
@ -812,36 +682,16 @@ function JobOutput({ job, eventRelatedSearchableKeys, eventSearchableKeys }) {
|
||||
/>
|
||||
</OutputHeader>
|
||||
<HostStatusBar counts={job.host_status_counts} />
|
||||
<SearchToolbar
|
||||
id="job_output-toolbar"
|
||||
clearAllFilters={handleRemoveAllSearchTerms}
|
||||
collapseListedFiltersBreakpoint="lg"
|
||||
clearFiltersButtonText={t`Clear all filters`}
|
||||
>
|
||||
<SearchToolbarContent>
|
||||
<ToolbarToggleGroup toggleIcon={<SearchIcon />} breakpoint="lg">
|
||||
<ToolbarItem variant="search-filter">
|
||||
{isJobRunning(job.status) ? (
|
||||
<Tooltip
|
||||
content={t`Search is disabled while the job is running`}
|
||||
>
|
||||
{renderSearchComponent()}
|
||||
</Tooltip>
|
||||
) : (
|
||||
renderSearchComponent()
|
||||
)}
|
||||
</ToolbarItem>
|
||||
</ToolbarToggleGroup>
|
||||
{isJobRunning(job.status) ? (
|
||||
<Button
|
||||
variant={isFollowModeEnabled ? 'secondary' : 'primary'}
|
||||
onClick={handleFollowToggle}
|
||||
>
|
||||
{isFollowModeEnabled ? t`Unfollow` : t`Follow`}
|
||||
</Button>
|
||||
) : null}
|
||||
</SearchToolbarContent>
|
||||
</SearchToolbar>
|
||||
<JobOutputSearch
|
||||
qsConfig={QS_CONFIG}
|
||||
job={job}
|
||||
eventRelatedSearchableKeys={eventRelatedSearchableKeys}
|
||||
eventSearchableKeys={eventSearchableKeys}
|
||||
remoteRowCount={remoteRowCount}
|
||||
scrollToRow={scrollToRow}
|
||||
isFollowModeEnabled={isFollowModeEnabled}
|
||||
setIsFollowModeEnabled={setIsFollowModeEnabled}
|
||||
/>
|
||||
<PageControls
|
||||
onScrollFirst={handleScrollFirst}
|
||||
onScrollLast={handleScrollLast}
|
||||
@ -946,5 +796,4 @@ function JobOutput({ job, eventRelatedSearchableKeys, eventSearchableKeys }) {
|
||||
);
|
||||
}
|
||||
|
||||
export { JobOutput as _JobOutput };
|
||||
export default JobOutput;
|
||||
|
||||
188
awx/ui/src/screens/Job/JobOutput/JobOutputSearch.js
Normal file
188
awx/ui/src/screens/Job/JobOutput/JobOutputSearch.js
Normal file
@ -0,0 +1,188 @@
|
||||
import React from 'react';
|
||||
import { useHistory, useLocation } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
import { t } from '@lingui/macro';
|
||||
import {
|
||||
Toolbar,
|
||||
ToolbarContent,
|
||||
ToolbarItem,
|
||||
ToolbarToggleGroup,
|
||||
Tooltip,
|
||||
Button,
|
||||
} from '@patternfly/react-core';
|
||||
import { SearchIcon } from '@patternfly/react-icons';
|
||||
import Search from 'components/Search';
|
||||
import {
|
||||
parseQueryString,
|
||||
mergeParams,
|
||||
removeParams,
|
||||
updateQueryString,
|
||||
} from 'util/qs';
|
||||
import { isJobRunning } from 'util/jobs';
|
||||
|
||||
const SearchToolbarContent = styled(ToolbarContent)`
|
||||
padding-left: 0px !important;
|
||||
padding-right: 0px !important;
|
||||
`;
|
||||
|
||||
function JobOutputSearch({
|
||||
qsConfig,
|
||||
job,
|
||||
eventRelatedSearchableKeys,
|
||||
eventSearchableKeys,
|
||||
remoteRowCount,
|
||||
scrollToRow,
|
||||
isFollowModeEnabled,
|
||||
setIsFollowModeEnabled,
|
||||
}) {
|
||||
const location = useLocation();
|
||||
const history = useHistory();
|
||||
|
||||
const handleSearch = (key, value) => {
|
||||
const params = parseQueryString(qsConfig, location.search);
|
||||
const qs = updateQueryString(
|
||||
qsConfig,
|
||||
location.search,
|
||||
mergeParams(params, { [key]: value })
|
||||
);
|
||||
pushHistoryState(qs);
|
||||
};
|
||||
|
||||
const handleReplaceSearch = (key, value) => {
|
||||
const qs = updateQueryString(qsConfig, location.search, {
|
||||
[key]: value,
|
||||
});
|
||||
pushHistoryState(qs);
|
||||
};
|
||||
|
||||
const handleRemoveSearchTerm = (key, value) => {
|
||||
const oldParams = parseQueryString(qsConfig, location.search);
|
||||
const updatedParams = removeParams(qsConfig, oldParams, {
|
||||
[key]: value,
|
||||
});
|
||||
const qs = updateQueryString(qsConfig, location.search, updatedParams);
|
||||
pushHistoryState(qs);
|
||||
};
|
||||
|
||||
const handleRemoveAllSearchTerms = () => {
|
||||
const oldParams = parseQueryString(qsConfig, location.search);
|
||||
Object.keys(oldParams).forEach((key) => {
|
||||
oldParams[key] = null;
|
||||
});
|
||||
const qs = updateQueryString(qsConfig, location.search, oldParams);
|
||||
pushHistoryState(qs);
|
||||
};
|
||||
|
||||
const pushHistoryState = (qs) => {
|
||||
const { pathname } = history.location;
|
||||
history.push(qs ? `${pathname}?${qs}` : pathname);
|
||||
};
|
||||
|
||||
const handleFollowToggle = () => {
|
||||
if (isFollowModeEnabled) {
|
||||
setIsFollowModeEnabled(false);
|
||||
} else {
|
||||
setIsFollowModeEnabled(true);
|
||||
scrollToRow(remoteRowCount - 1);
|
||||
}
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
name: t`Stdout`,
|
||||
key: 'stdout__icontains',
|
||||
isDefault: true,
|
||||
},
|
||||
{
|
||||
name: t`Event`,
|
||||
key: 'event',
|
||||
options: [
|
||||
['runner_on_failed', t`Host Failed`],
|
||||
['runner_on_start', t`Host Started`],
|
||||
['runner_on_ok', t`Host OK`],
|
||||
['runner_on_error', t`Host Failure`],
|
||||
['runner_on_skipped', t`Host Skipped`],
|
||||
['runner_on_unreachable', t`Host Unreachable`],
|
||||
['runner_on_no_hosts', t`No Hosts Remaining`],
|
||||
['runner_on_async_poll', t`Host Polling`],
|
||||
['runner_on_async_ok', t`Host Async OK`],
|
||||
['runner_on_async_failed', t`Host Async Failure`],
|
||||
['runner_item_on_ok', t`Item OK`],
|
||||
['runner_item_on_failed', t`Item Failed`],
|
||||
['runner_item_on_skipped', t`Item Skipped`],
|
||||
['runner_retry', t`Host Retry`],
|
||||
['runner_on_file_diff', t`File Difference`],
|
||||
['playbook_on_start', t`Playbook Started`],
|
||||
['playbook_on_notify', t`Running Handlers`],
|
||||
['playbook_on_include', t`Including File`],
|
||||
['playbook_on_no_hosts_matched', t`No Hosts Matched`],
|
||||
['playbook_on_no_hosts_remaining', t`No Hosts Remaining`],
|
||||
['playbook_on_task_start', t`Task Started`],
|
||||
['playbook_on_vars_prompt', t`Variables Prompted`],
|
||||
['playbook_on_setup', t`Gathering Facts`],
|
||||
['playbook_on_play_start', t`Play Started`],
|
||||
['playbook_on_stats', t`Playbook Complete`],
|
||||
['debug', t`Debug`],
|
||||
['verbose', t`Verbose`],
|
||||
['deprecated', t`Deprecated`],
|
||||
['warning', t`Warning`],
|
||||
['system_warning', t`System Warning`],
|
||||
['error', t`Error`],
|
||||
],
|
||||
},
|
||||
{ name: t`Advanced`, key: 'advanced' },
|
||||
];
|
||||
const isDisabled = isJobRunning(job.status);
|
||||
|
||||
return (
|
||||
<Toolbar
|
||||
id="job_output-toolbar"
|
||||
clearAllFilters={handleRemoveAllSearchTerms}
|
||||
collapseListedFiltersBreakpoint="lg"
|
||||
clearFiltersButtonText={t`Clear all filters`}
|
||||
>
|
||||
<SearchToolbarContent>
|
||||
<ToolbarToggleGroup toggleIcon={<SearchIcon />} breakpoint="lg">
|
||||
<ToolbarItem variant="search-filter">
|
||||
{isDisabled ? (
|
||||
<Tooltip content={t`Search is disabled while the job is running`}>
|
||||
<Search
|
||||
qsConfig={qsConfig}
|
||||
columns={columns}
|
||||
searchableKeys={eventSearchableKeys}
|
||||
relatedSearchableKeys={eventRelatedSearchableKeys}
|
||||
onSearch={handleSearch}
|
||||
onReplaceSearch={handleReplaceSearch}
|
||||
onShowAdvancedSearch={() => {}}
|
||||
onRemove={handleRemoveSearchTerm}
|
||||
isDisabled
|
||||
/>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Search
|
||||
qsConfig={qsConfig}
|
||||
columns={columns}
|
||||
searchableKeys={eventSearchableKeys}
|
||||
relatedSearchableKeys={eventRelatedSearchableKeys}
|
||||
onSearch={handleSearch}
|
||||
onReplaceSearch={handleReplaceSearch}
|
||||
onShowAdvancedSearch={() => {}}
|
||||
onRemove={handleRemoveSearchTerm}
|
||||
/>
|
||||
)}
|
||||
</ToolbarItem>
|
||||
</ToolbarToggleGroup>
|
||||
{isJobRunning(job.status) ? (
|
||||
<Button
|
||||
variant={isFollowModeEnabled ? 'secondary' : 'primary'}
|
||||
onClick={handleFollowToggle}
|
||||
>
|
||||
{isFollowModeEnabled ? t`Unfollow` : t`Follow`}
|
||||
</Button>
|
||||
) : null}
|
||||
</SearchToolbarContent>
|
||||
</Toolbar>
|
||||
);
|
||||
}
|
||||
|
||||
export default JobOutputSearch;
|
||||
@ -2,7 +2,7 @@ import 'styled-components/macro';
|
||||
import React from 'react';
|
||||
|
||||
import { t } from '@lingui/macro';
|
||||
import { Button as PFButton } from '@patternfly/react-core';
|
||||
import { Button } from '@patternfly/react-core';
|
||||
import {
|
||||
AngleDoubleUpIcon,
|
||||
AngleDoubleDownIcon,
|
||||
@ -14,16 +14,11 @@ import styled from 'styled-components';
|
||||
const Wrapper = styled.div`
|
||||
display: flex;
|
||||
height: 35px;
|
||||
outline: 1px solid #d7d7d7;
|
||||
border: 1px solid #d7d7d7;
|
||||
width: 100%;
|
||||
justify-content: flex-end;
|
||||
`;
|
||||
|
||||
const Button = styled(PFButton)`
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
`;
|
||||
|
||||
const PageControls = ({
|
||||
onScrollFirst,
|
||||
onScrollLast,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user