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={
<HeaderRow qsConfig={qsConfig}> <HeaderRow qsConfig={qsConfig} isExpandable>
<HeaderCell sortKey="name">{i18n._(t`Name`)}</HeaderCell> <HeaderCell sortKey="name">{i18n._(t`Name`)}</HeaderCell>
<HeaderCell sortKey="status">{i18n._(t`Status`)}</HeaderCell> <HeaderCell sortKey="status">{i18n._(t`Status`)}</HeaderCell>
{showTypeColumn && <HeaderCell>{i18n._(t`Type`)}</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 { Link } from 'react-router-dom';
import { withI18n } from '@lingui/react'; import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { Button } from '@patternfly/react-core'; import { Button, Chip } from '@patternfly/react-core';
import { Tr, Td } from '@patternfly/react-table'; import { Tr, Td, ExpandableRowContent } from '@patternfly/react-table';
import { RocketIcon } from '@patternfly/react-icons'; import { RocketIcon } from '@patternfly/react-icons';
import { ActionsTd, ActionItem } from '../PaginatedTable'; import { ActionsTd, ActionItem } from '../PaginatedTable';
import LaunchButton from '../LaunchButton'; import LaunchButton from '../LaunchButton';
import StatusLabel from '../StatusLabel'; import StatusLabel from '../StatusLabel';
import { DetailList, Detail } from '../DetailList';
import ChipGroup from '../ChipGroup';
import CredentialChip from '../CredentialChip';
import { formatDateString } from '../../util/dates'; import { formatDateString } from '../../util/dates';
import { JOB_TYPE_URL_SEGMENTS } from '../../constants'; 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({ function JobListItem({
i18n, i18n,
job, job,
@@ -20,6 +52,7 @@ function JobListItem({
showTypeColumn = false, showTypeColumn = false,
}) { }) {
const labelId = `check-action-${job.id}`; const labelId = `check-action-${job.id}`;
const [isExpanded, setIsExpanded] = useState(false);
const jobTypes = { const jobTypes = {
project_update: i18n._(t`Source Control Update`), project_update: i18n._(t`Source Control Update`),
@@ -30,8 +63,20 @@ function JobListItem({
workflow_job: i18n._(t`Workflow Job`), workflow_job: i18n._(t`Workflow Job`),
}; };
const { value: launchedByValue, link: launchedByLink } =
getLaunchedByDetails(job) || {};
const { credentials, inventory, labels } = job.summary_fields;
return ( return (
<>
<Tr id={`job-row-${job.id}`}> <Tr id={`job-row-${job.id}`}>
<Td
expand={{
rowIndex: job.id,
isExpanded,
onToggle: () => setIsExpanded(!isExpanded),
}}
/>
<Td <Td
select={{ select={{
rowIndex, rowIndex,
@@ -55,7 +100,9 @@ function JobListItem({
{showTypeColumn && ( {showTypeColumn && (
<Td dataLabel={i18n._(t`Type`)}>{jobTypes[job.type]}</Td> <Td dataLabel={i18n._(t`Type`)}>{jobTypes[job.type]}</Td>
)} )}
<Td dataLabel={i18n._(t`Start Time`)}>{formatDateString(job.started)}</Td> <Td dataLabel={i18n._(t`Start Time`)}>
{formatDateString(job.started)}
</Td>
<Td dataLabel={i18n._(t`Finish Time`)}> <Td dataLabel={i18n._(t`Finish Time`)}>
{job.finished ? formatDateString(job.finished) : ''} {job.finished ? formatDateString(job.finished) : ''}
</Td> </Td>
@@ -81,6 +128,77 @@ function JobListItem({
</ActionItem> </ActionItem>
</ActionsTd> </ActionsTd>
</Tr> </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; --pf-c-table--cell--Overflow: initial;
`; `;
export default function HeaderRow({ qsConfig, children }) { export default function HeaderRow({ qsConfig, isExpandable, children }) {
const location = useLocation(); const location = useLocation();
const history = useHistory(); const history = useHistory();
@@ -46,6 +46,7 @@ export default function HeaderRow({ qsConfig, children }) {
return ( return (
<Thead> <Thead>
<Tr> <Tr>
{isExpandable && <Th />}
<Th /> <Th />
{React.Children.map( {React.Children.map(
children, children,

View File

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