add expandable rows to JobList

This commit is contained in:
Keith Grant 2020-12-23 14:36:54 -08:00
parent da16785201
commit 8bde6060c4
4 changed files with 175 additions and 55 deletions

View File

@ -234,7 +234,7 @@ function JobList({ i18n, defaultParams, showTypeColumn = false }) {
},
]}
headerRow={
<HeaderRow qsConfig={qsConfig}>
<HeaderRow qsConfig={qsConfig} isExpandable>
<HeaderCell sortKey="name">{i18n._(t`Name`)}</HeaderCell>
<HeaderCell sortKey="status">{i18n._(t`Status`)}</HeaderCell>
{showTypeColumn && <HeaderCell>{i18n._(t`Type`)}</HeaderCell>}

View File

@ -1,16 +1,48 @@
import React from 'react';
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { Button } from '@patternfly/react-core';
import { Tr, Td } from '@patternfly/react-table';
import { Button, Chip } from '@patternfly/react-core';
import { Tr, Td, ExpandableRowContent } from '@patternfly/react-table';
import { RocketIcon } from '@patternfly/react-icons';
import { ActionsTd, ActionItem } from '../PaginatedTable';
import LaunchButton from '../LaunchButton';
import StatusLabel from '../StatusLabel';
import { DetailList, Detail } from '../DetailList';
import ChipGroup from '../ChipGroup';
import CredentialChip from '../CredentialChip';
import { formatDateString } from '../../util/dates';
import { JOB_TYPE_URL_SEGMENTS } from '../../constants';
const getLaunchedByDetails = ({ summary_fields = {}, related = {} }) => {
const {
created_by: createdBy,
job_template: jobTemplate,
schedule,
} = summary_fields;
const { schedule: relatedSchedule } = related;
if (!createdBy && !schedule) {
return {};
}
let link;
let value;
if (createdBy) {
link = `/users/${createdBy.id}`;
value = createdBy.username;
} else if (relatedSchedule && jobTemplate) {
link = `/templates/job_template/${jobTemplate.id}/schedules/${schedule.id}`;
value = schedule.name;
} else {
link = null;
value = schedule.name;
}
return { link, value };
};
function JobListItem({
i18n,
job,
@ -20,6 +52,7 @@ function JobListItem({
showTypeColumn = false,
}) {
const labelId = `check-action-${job.id}`;
const [isExpanded, setIsExpanded] = useState(false);
const jobTypes = {
project_update: i18n._(t`Source Control Update`),
@ -30,57 +63,142 @@ function JobListItem({
workflow_job: i18n._(t`Workflow Job`),
};
const { value: launchedByValue, link: launchedByLink } =
getLaunchedByDetails(job) || {};
const { credentials, inventory, labels } = job.summary_fields;
return (
<Tr id={`job-row-${job.id}`}>
<Td
select={{
rowIndex,
isSelected,
onSelect,
disable: false,
}}
/>
<Td id={labelId} dataLabel={i18n._(t`Name`)}>
<span>
<Link to={`/jobs/${JOB_TYPE_URL_SEGMENTS[job.type]}/${job.id}`}>
<b>
{job.id} &mdash; {job.name}
</b>
</Link>
</span>
</Td>
<Td dataLabel={i18n._(t`Status`)}>
{job.status && <StatusLabel status={job.status} />}
</Td>
{showTypeColumn && (
<Td dataLabel={i18n._(t`Type`)}>{jobTypes[job.type]}</Td>
)}
<Td dataLabel={i18n._(t`Start Time`)}>{formatDateString(job.started)}</Td>
<Td dataLabel={i18n._(t`Finish Time`)}>
{job.finished ? formatDateString(job.finished) : ''}
</Td>
<ActionsTd dataLabel={i18n._(t`Actions`)}>
<ActionItem
visible={
job.type !== 'system_job' &&
job.summary_fields?.user_capabilities?.start
}
tooltip={i18n._(t`Relaunch Job`)}
>
<LaunchButton resource={job}>
{({ handleRelaunch }) => (
<Button
variant="plain"
onClick={handleRelaunch}
aria-label={i18n._(t`Relaunch`)}
>
<RocketIcon />
</Button>
)}
</LaunchButton>
</ActionItem>
</ActionsTd>
</Tr>
<>
<Tr id={`job-row-${job.id}`}>
<Td
expand={{
rowIndex: job.id,
isExpanded,
onToggle: () => setIsExpanded(!isExpanded),
}}
/>
<Td
select={{
rowIndex,
isSelected,
onSelect,
disable: false,
}}
/>
<Td id={labelId} dataLabel={i18n._(t`Name`)}>
<span>
<Link to={`/jobs/${JOB_TYPE_URL_SEGMENTS[job.type]}/${job.id}`}>
<b>
{job.id} &mdash; {job.name}
</b>
</Link>
</span>
</Td>
<Td dataLabel={i18n._(t`Status`)}>
{job.status && <StatusLabel status={job.status} />}
</Td>
{showTypeColumn && (
<Td dataLabel={i18n._(t`Type`)}>{jobTypes[job.type]}</Td>
)}
<Td dataLabel={i18n._(t`Start Time`)}>
{formatDateString(job.started)}
</Td>
<Td dataLabel={i18n._(t`Finish Time`)}>
{job.finished ? formatDateString(job.finished) : ''}
</Td>
<ActionsTd dataLabel={i18n._(t`Actions`)}>
<ActionItem
visible={
job.type !== 'system_job' &&
job.summary_fields?.user_capabilities?.start
}
tooltip={i18n._(t`Relaunch Job`)}
>
<LaunchButton resource={job}>
{({ handleRelaunch }) => (
<Button
variant="plain"
onClick={handleRelaunch}
aria-label={i18n._(t`Relaunch`)}
>
<RocketIcon />
</Button>
)}
</LaunchButton>
</ActionItem>
</ActionsTd>
</Tr>
<Tr isExpanded={isExpanded}>
<Td colSpan={2} />
<Td colSpan={showTypeColumn ? 5 : 4}>
<ExpandableRowContent>
<DetailList>
<Detail
label={i18n._(t`Started`)}
value={formatDateString(job.started)}
/>
<Detail
label={i18n._(t`Finished`)}
value={formatDateString(job.started)}
/>
<Detail
label={i18n._(t`Launched By`)}
value={
launchedByLink ? (
<Link to={`${launchedByLink}`}>{launchedByValue}</Link>
) : (
launchedByValue
)
}
/>
{credentials && credentials.length > 0 && (
<Detail
fullWidth
label={i18n._(t`Credentials`)}
value={
<ChipGroup numChips={5} totalChips={credentials.length}>
{credentials.map(c => (
<CredentialChip key={c.id} credential={c} isReadOnly />
))}
</ChipGroup>
}
/>
)}
{labels && labels.count > 0 && (
<Detail
label={i18n._(t`Labels`)}
value={
<ChipGroup numChips={5} totalChips={labels.results.length}>
{labels.results.map(l => (
<Chip key={l.id} isReadOnly>
{l.name}
</Chip>
))}
</ChipGroup>
}
/>
)}
{inventory && (
<Detail
label={i18n._(t`Inventory`)}
value={
<Link
to={
inventory.kind === 'smart'
? `/inventories/smart_inventory/${inventory.id}`
: `/inventories/inventory/${inventory.id}`
}
>
{inventory.name}
</Link>
}
/>
)}
</DetailList>
</ExpandableRowContent>
</Td>
</Tr>
</>
);
}

View File

@ -12,7 +12,7 @@ const Th = styled(PFTh)`
--pf-c-table--cell--Overflow: initial;
`;
export default function HeaderRow({ qsConfig, children }) {
export default function HeaderRow({ qsConfig, isExpandable, children }) {
const location = useLocation();
const history = useHistory();
@ -46,6 +46,7 @@ export default function HeaderRow({ qsConfig, children }) {
return (
<Thead>
<Tr>
{isExpandable && <Th />}
<Th />
{React.Children.map(
children,

View File

@ -360,3 +360,4 @@ JobDetail.propTypes = {
};
export default withI18n()(JobDetail);
export { getLaunchedByDetails };