mirror of
https://github.com/ansible/awx.git
synced 2026-02-18 11:40:05 -03:30
Merge pull request #11858 from keithjgrant/11409-empty-job-output
add JobOutput screens for empty content
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Title,
|
Title,
|
||||||
EmptyState,
|
EmptyState,
|
||||||
@@ -9,9 +8,14 @@ import {
|
|||||||
} from '@patternfly/react-core';
|
} from '@patternfly/react-core';
|
||||||
import { CubesIcon } from '@patternfly/react-icons';
|
import { CubesIcon } from '@patternfly/react-icons';
|
||||||
|
|
||||||
const ContentEmpty = ({ title = '', message = '' }) => (
|
const ContentEmpty = ({
|
||||||
<EmptyState variant="full">
|
title = '',
|
||||||
<EmptyStateIcon icon={CubesIcon} />
|
message = '',
|
||||||
|
icon = CubesIcon,
|
||||||
|
className = '',
|
||||||
|
}) => (
|
||||||
|
<EmptyState variant="full" className={className}>
|
||||||
|
<EmptyStateIcon icon={icon} />
|
||||||
<Title size="lg" headingLevel="h3">
|
<Title size="lg" headingLevel="h3">
|
||||||
{title || t`No items found.`}
|
{title || t`No items found.`}
|
||||||
</Title>
|
</Title>
|
||||||
|
|||||||
36
awx/ui/src/screens/Job/JobOutput/EmptyOutput.js
Normal file
36
awx/ui/src/screens/Job/JobOutput/EmptyOutput.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import 'styled-components/macro';
|
||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import { SearchIcon } from '@patternfly/react-icons';
|
||||||
|
import ContentEmpty from 'components/ContentEmpty';
|
||||||
|
|
||||||
|
export default function EmptyOutput({
|
||||||
|
hasQueryParams,
|
||||||
|
isJobRunning,
|
||||||
|
onUnmount,
|
||||||
|
}) {
|
||||||
|
let title;
|
||||||
|
let message;
|
||||||
|
let icon;
|
||||||
|
|
||||||
|
useEffect(() => onUnmount);
|
||||||
|
|
||||||
|
if (hasQueryParams) {
|
||||||
|
title = t`The search filter did not produce any results…`;
|
||||||
|
message = t`Please try another search using the filter above`;
|
||||||
|
icon = SearchIcon;
|
||||||
|
} else if (isJobRunning) {
|
||||||
|
title = t`Waiting for job output…`;
|
||||||
|
} else {
|
||||||
|
title = t`No output found for this job.`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ContentEmpty
|
||||||
|
css="height: 100%"
|
||||||
|
title={title}
|
||||||
|
message={message}
|
||||||
|
icon={icon}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -17,11 +17,15 @@ function JobEvent({
|
|||||||
isCollapsed,
|
isCollapsed,
|
||||||
onToggleCollapsed,
|
onToggleCollapsed,
|
||||||
hasChildren,
|
hasChildren,
|
||||||
|
jobStatus,
|
||||||
}) {
|
}) {
|
||||||
const numOutputLines = lineTextHtml?.length || 0;
|
const numOutputLines = lineTextHtml?.length || 0;
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
measure();
|
const timeout = setTimeout(measure, 0);
|
||||||
}, [numOutputLines, isCollapsed, measure]);
|
return () => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
};
|
||||||
|
}, [numOutputLines, isCollapsed, measure, jobStatus]);
|
||||||
|
|
||||||
let toggleLineIndex = -1;
|
let toggleLineIndex = -1;
|
||||||
if (hasChildren) {
|
if (hasChildren) {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import JobEventSkeleton from './JobEventSkeleton';
|
|||||||
import PageControls from './PageControls';
|
import PageControls from './PageControls';
|
||||||
import HostEventModal from './HostEventModal';
|
import HostEventModal from './HostEventModal';
|
||||||
import JobOutputSearch from './JobOutputSearch';
|
import JobOutputSearch from './JobOutputSearch';
|
||||||
|
import EmptyOutput from './EmptyOutput';
|
||||||
import { HostStatusBar, OutputToolbar } from './shared';
|
import { HostStatusBar, OutputToolbar } from './shared';
|
||||||
import getLineTextHtml from './getLineTextHtml';
|
import getLineTextHtml from './getLineTextHtml';
|
||||||
import connectJobSocket, { closeWebSocket } from './connectJobSocket';
|
import connectJobSocket, { closeWebSocket } from './connectJobSocket';
|
||||||
@@ -220,6 +221,7 @@ function JobOutput({ job, eventRelatedSearchableKeys, eventSearchableKeys }) {
|
|||||||
...Object.values(siblingRequests.current || {}),
|
...Object.values(siblingRequests.current || {}),
|
||||||
...Object.values(numEventsRequests.current || {}),
|
...Object.values(numEventsRequests.current || {}),
|
||||||
];
|
];
|
||||||
|
setHasContentLoading(true); // prevents "no content found" screen from flashing
|
||||||
Promise.all(pendingRequests).then(() => {
|
Promise.all(pendingRequests).then(() => {
|
||||||
setRemoteRowCount(0);
|
setRemoteRowCount(0);
|
||||||
clearLoadedEvents();
|
clearLoadedEvents();
|
||||||
@@ -509,6 +511,7 @@ function JobOutput({ job, eventRelatedSearchableKeys, eventSearchableKeys }) {
|
|||||||
onToggleCollapsed={() => {
|
onToggleCollapsed={() => {
|
||||||
toggleNodeIsCollapsed(event.uuid, !node.isCollapsed);
|
toggleNodeIsCollapsed(event.uuid, !node.isCollapsed);
|
||||||
}}
|
}}
|
||||||
|
jobStatus={jobStatus}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<JobEventSkeleton
|
<JobEventSkeleton
|
||||||
@@ -715,36 +718,54 @@ function JobOutput({ job, eventRelatedSearchableKeys, eventSearchableKeys }) {
|
|||||||
rowCount={totalNonCollapsedRows + wsEvents.length}
|
rowCount={totalNonCollapsedRows + wsEvents.length}
|
||||||
minimumBatchSize={50}
|
minimumBatchSize={50}
|
||||||
>
|
>
|
||||||
{({ onRowsRendered, registerChild }) => (
|
{({ onRowsRendered, registerChild }) => {
|
||||||
<AutoSizer nonce={window.NONCE_ID} onResize={handleResize}>
|
if (
|
||||||
{({ width, height }) => (
|
!hasContentLoading &&
|
||||||
<>
|
remoteRowCount + wsEvents.length === 0
|
||||||
{hasContentLoading ? (
|
) {
|
||||||
<div style={{ width }}>
|
return (
|
||||||
<ContentLoading />
|
<EmptyOutput
|
||||||
</div>
|
hasQueryParams={location.search.length > 1}
|
||||||
) : (
|
isJobRunning={isJobRunning(jobStatus)}
|
||||||
<List
|
onUnmount={() => {
|
||||||
ref={(ref) => {
|
if (listRef.current?.recomputeRowHeights) {
|
||||||
registerChild(ref);
|
listRef.current.recomputeRowHeights();
|
||||||
listRef.current = ref;
|
}
|
||||||
}}
|
}}
|
||||||
deferredMeasurementCache={cache}
|
/>
|
||||||
height={height || 1}
|
);
|
||||||
onRowsRendered={onRowsRendered}
|
}
|
||||||
rowCount={totalNonCollapsedRows + wsEvents.length}
|
return (
|
||||||
rowHeight={cache.rowHeight}
|
<AutoSizer nonce={window.NONCE_ID} onResize={handleResize}>
|
||||||
rowRenderer={rowRenderer}
|
{({ width, height }) => (
|
||||||
scrollToAlignment="start"
|
<>
|
||||||
width={width || 1}
|
{hasContentLoading ? (
|
||||||
overscanRowCount={20}
|
<div style={{ width }}>
|
||||||
onScroll={handleScroll}
|
<ContentLoading />
|
||||||
/>
|
</div>
|
||||||
)}
|
) : (
|
||||||
</>
|
<List
|
||||||
)}
|
ref={(ref) => {
|
||||||
</AutoSizer>
|
registerChild(ref);
|
||||||
)}
|
listRef.current = ref;
|
||||||
|
}}
|
||||||
|
deferredMeasurementCache={cache}
|
||||||
|
height={height || 1}
|
||||||
|
onRowsRendered={onRowsRendered}
|
||||||
|
rowCount={totalNonCollapsedRows + wsEvents.length}
|
||||||
|
rowHeight={cache.rowHeight}
|
||||||
|
rowRenderer={rowRenderer}
|
||||||
|
scrollToAlignment="start"
|
||||||
|
width={width || 1}
|
||||||
|
overscanRowCount={20}
|
||||||
|
onScroll={handleScroll}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</AutoSizer>
|
||||||
|
);
|
||||||
|
}}
|
||||||
</InfiniteLoader>
|
</InfiniteLoader>
|
||||||
<OutputFooter />
|
<OutputFooter />
|
||||||
</OutputWrapper>
|
</OutputWrapper>
|
||||||
|
|||||||
Reference in New Issue
Block a user