mirror of
https://github.com/ansible/awx.git
synced 2026-04-06 18:49:21 -02:30
add expandable row details to template list
This commit is contained in:
@@ -208,40 +208,14 @@ function TemplateList({ i18n }) {
|
|||||||
key: 'modified_by__username__icontains',
|
key: 'modified_by__username__icontains',
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
toolbarSortColumns={[
|
|
||||||
{
|
|
||||||
name: i18n._(t`Inventory`),
|
|
||||||
key: 'job_template__inventory__id',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: i18n._(t`Last Job Run`),
|
|
||||||
key: 'last_job_run',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: i18n._(t`Modified`),
|
|
||||||
key: 'modified',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: i18n._(t`Name`),
|
|
||||||
key: 'name',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: i18n._(t`Project`),
|
|
||||||
key: 'jobtemplate__project__id',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: i18n._(t`Type`),
|
|
||||||
key: 'type',
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
toolbarSearchableKeys={searchableKeys}
|
toolbarSearchableKeys={searchableKeys}
|
||||||
toolbarRelatedSearchableKeys={relatedSearchableKeys}
|
toolbarRelatedSearchableKeys={relatedSearchableKeys}
|
||||||
headerRow={
|
headerRow={
|
||||||
<HeaderRow qsConfig={QS_CONFIG}>
|
<HeaderRow qsConfig={QS_CONFIG} isExpandable>
|
||||||
<HeaderCell sortKey="name">{i18n._(t`Name`)}</HeaderCell>
|
<HeaderCell sortKey="name">{i18n._(t`Name`)}</HeaderCell>
|
||||||
<HeaderCell sortKey="type">{i18n._(t`Type`)}</HeaderCell>
|
<HeaderCell sortKey="type">{i18n._(t`Type`)}</HeaderCell>
|
||||||
<HeaderCell sortKey="last_job_run">
|
<HeaderCell sortKey="last_job_run">
|
||||||
{i18n._(t`Recent Jobs`)}
|
{i18n._(t`Last Run`)}
|
||||||
</HeaderCell>
|
</HeaderCell>
|
||||||
</HeaderRow>
|
</HeaderRow>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import 'styled-components/macro';
|
import 'styled-components/macro';
|
||||||
import React, { useState, useCallback } from 'react';
|
import React, { useState, useCallback } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { Button, Tooltip } from '@patternfly/react-core';
|
import { Button, Tooltip, Chip } from '@patternfly/react-core';
|
||||||
import { Tr, Td } from '@patternfly/react-table';
|
import { Tr, Td, ExpandableRowContent } from '@patternfly/react-table';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import {
|
import {
|
||||||
@@ -12,6 +12,13 @@ import {
|
|||||||
RocketIcon,
|
RocketIcon,
|
||||||
} from '@patternfly/react-icons';
|
} from '@patternfly/react-icons';
|
||||||
import { ActionsTd, ActionItem } from '../../../components/PaginatedTable';
|
import { ActionsTd, ActionItem } from '../../../components/PaginatedTable';
|
||||||
|
import {
|
||||||
|
DetailList,
|
||||||
|
Detail,
|
||||||
|
DeletedDetail,
|
||||||
|
} from '../../../components/DetailList';
|
||||||
|
import ChipGroup from '../../../components/ChipGroup';
|
||||||
|
import CredentialChip from '../../../components/CredentialChip';
|
||||||
import { timeOfDay } from '../../../util/dates';
|
import { timeOfDay } from '../../../util/dates';
|
||||||
|
|
||||||
import { JobTemplatesAPI, WorkflowJobTemplatesAPI } from '../../../api';
|
import { JobTemplatesAPI, WorkflowJobTemplatesAPI } from '../../../api';
|
||||||
@@ -29,6 +36,7 @@ function TemplateListItem({
|
|||||||
fetchTemplates,
|
fetchTemplates,
|
||||||
rowIndex,
|
rowIndex,
|
||||||
}) {
|
}) {
|
||||||
|
const [isExpanded, setIsExpanded] = useState(false);
|
||||||
const [isDisabled, setIsDisabled] = useState(false);
|
const [isDisabled, setIsDisabled] = useState(false);
|
||||||
const labelId = `check-action-${template.id}`;
|
const labelId = `check-action-${template.id}`;
|
||||||
|
|
||||||
@@ -53,102 +61,203 @@ function TemplateListItem({
|
|||||||
setIsDisabled(false);
|
setIsDisabled(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const {
|
||||||
|
summary_fields: summaryFields,
|
||||||
|
ask_inventory_on_launch: askInventoryOnLaunch,
|
||||||
|
} = template;
|
||||||
|
|
||||||
const missingResourceIcon =
|
const missingResourceIcon =
|
||||||
template.type === 'job_template' &&
|
template.type === 'job_template' &&
|
||||||
(!template.summary_fields.project ||
|
(!summaryFields.project ||
|
||||||
(!template.summary_fields.inventory &&
|
(!summaryFields.inventory && !askInventoryOnLaunch));
|
||||||
!template.ask_inventory_on_launch));
|
|
||||||
return (
|
const inventoryValue = (kind, id) => {
|
||||||
<Tr id={`template-row-${template.id}`}>
|
const inventorykind = kind === 'smart' ? 'smart_inventory' : 'inventory';
|
||||||
<Td
|
|
||||||
select={{
|
return askInventoryOnLaunch ? (
|
||||||
rowIndex,
|
<>
|
||||||
isSelected,
|
<Link to={`/inventories/${inventorykind}/${id}/details`}>
|
||||||
onSelect,
|
{summaryFields.inventory.name}
|
||||||
}}
|
|
||||||
dataLabel={i18n._(t`Selected`)}
|
|
||||||
/>
|
|
||||||
<Td id={labelId} dataLabel={i18n._(t`Name`)}>
|
|
||||||
<Link to={`${detailUrl}`}>
|
|
||||||
<b>{template.name}</b>
|
|
||||||
</Link>
|
</Link>
|
||||||
{missingResourceIcon && (
|
<span> {i18n._(t`(Prompt on launch)`)}</span>
|
||||||
<span>
|
</>
|
||||||
<Tooltip
|
) : (
|
||||||
content={i18n._(t`Resources are missing from this template.`)}
|
<Link to={`/inventories/${inventorykind}/${id}/details`}>
|
||||||
position="right"
|
{summaryFields.inventory.name}
|
||||||
>
|
</Link>
|
||||||
<ExclamationTriangleIcon css="color: #c9190b; margin-left: 20px;" />
|
);
|
||||||
</Tooltip>
|
};
|
||||||
</span>
|
|
||||||
)}
|
return (
|
||||||
</Td>
|
<>
|
||||||
<Td dataLabel={i18n._(t`Type`)}>{toTitleCase(template.type)}</Td>
|
<Tr id={`template-row-${template.id}`}>
|
||||||
<Td dataLabel={i18n._(t`Recent Jobs`)}>
|
<Td
|
||||||
<Sparkline jobs={template.summary_fields.recent_jobs} />
|
expand={{
|
||||||
</Td>
|
rowIndex,
|
||||||
<ActionsTd dataLabel={i18n._(t`Actions`)}>
|
isExpanded,
|
||||||
<ActionItem
|
onToggle: () => setIsExpanded(!isExpanded),
|
||||||
visible={template.type === 'workflow_job_template'}
|
}}
|
||||||
tooltip={i18n._(t`Visualizer`)}
|
/>
|
||||||
>
|
<Td
|
||||||
<Button
|
select={{
|
||||||
isDisabled={isDisabled}
|
rowIndex,
|
||||||
aria-label={i18n._(t`Visualizer`)}
|
isSelected,
|
||||||
variant="plain"
|
onSelect,
|
||||||
component={Link}
|
}}
|
||||||
to={`/templates/workflow_job_template/${template.id}/visualizer`}
|
dataLabel={i18n._(t`Selected`)}
|
||||||
>
|
/>
|
||||||
<ProjectDiagramIcon />
|
<Td id={labelId} dataLabel={i18n._(t`Name`)}>
|
||||||
</Button>
|
<Link to={`${detailUrl}`}>
|
||||||
</ActionItem>
|
<b>{template.name}</b>
|
||||||
<ActionItem
|
</Link>
|
||||||
visible={template.summary_fields.user_capabilities.start}
|
{missingResourceIcon && (
|
||||||
tooltip={i18n._(t`Launch Template`)}
|
<span>
|
||||||
>
|
<Tooltip
|
||||||
<LaunchButton resource={template}>
|
content={i18n._(t`Resources are missing from this template.`)}
|
||||||
{({ handleLaunch }) => (
|
position="right"
|
||||||
<Button
|
|
||||||
isDisabled={isDisabled}
|
|
||||||
aria-label={i18n._(t`Launch template`)}
|
|
||||||
variant="plain"
|
|
||||||
onClick={handleLaunch}
|
|
||||||
>
|
>
|
||||||
<RocketIcon />
|
<ExclamationTriangleIcon css="color: #c9190b; margin-left: 20px;" />
|
||||||
</Button>
|
</Tooltip>
|
||||||
)}
|
</span>
|
||||||
</LaunchButton>
|
)}
|
||||||
</ActionItem>
|
</Td>
|
||||||
<ActionItem
|
<Td dataLabel={i18n._(t`Type`)}>{toTitleCase(template.type)}</Td>
|
||||||
visible={template.summary_fields.user_capabilities.edit}
|
<Td dataLabel={i18n._(t`Recent Jobs`)}>
|
||||||
tooltip={i18n._(t`Edit Template`)}
|
<Sparkline jobs={template.summary_fields.recent_jobs} />
|
||||||
>
|
</Td>
|
||||||
<Button
|
<ActionsTd dataLabel={i18n._(t`Actions`)}>
|
||||||
isDisabled={isDisabled}
|
<ActionItem
|
||||||
aria-label={i18n._(t`Edit Template`)}
|
visible={template.type === 'workflow_job_template'}
|
||||||
variant="plain"
|
tooltip={i18n._(t`Visualizer`)}
|
||||||
component={Link}
|
|
||||||
to={`/templates/${template.type}/${template.id}/edit`}
|
|
||||||
>
|
>
|
||||||
<PencilAltIcon />
|
<Button
|
||||||
</Button>
|
isDisabled={isDisabled}
|
||||||
</ActionItem>
|
aria-label={i18n._(t`Visualizer`)}
|
||||||
<ActionItem
|
variant="plain"
|
||||||
visible={template.summary_fields.user_capabilities.copy}
|
component={Link}
|
||||||
tooltip={i18n._(t`Copy Template`)}
|
to={`/templates/workflow_job_template/${template.id}/visualizer`}
|
||||||
>
|
>
|
||||||
<CopyButton
|
<ProjectDiagramIcon />
|
||||||
helperText={{
|
</Button>
|
||||||
errorMessage: i18n._(t`Failed to copy template.`),
|
</ActionItem>
|
||||||
}}
|
<ActionItem
|
||||||
isDisabled={isDisabled}
|
visible={template.summary_fields.user_capabilities.start}
|
||||||
onCopyStart={handleCopyStart}
|
tooltip={i18n._(t`Launch Template`)}
|
||||||
onCopyFinish={handleCopyFinish}
|
>
|
||||||
copyItem={copyTemplate}
|
<LaunchButton resource={template}>
|
||||||
/>
|
{({ handleLaunch }) => (
|
||||||
</ActionItem>
|
<Button
|
||||||
</ActionsTd>
|
isDisabled={isDisabled}
|
||||||
</Tr>
|
aria-label={i18n._(t`Launch template`)}
|
||||||
|
variant="plain"
|
||||||
|
onClick={handleLaunch}
|
||||||
|
>
|
||||||
|
<RocketIcon />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</LaunchButton>
|
||||||
|
</ActionItem>
|
||||||
|
<ActionItem
|
||||||
|
visible={template.summary_fields.user_capabilities.edit}
|
||||||
|
tooltip={i18n._(t`Edit Template`)}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
isDisabled={isDisabled}
|
||||||
|
aria-label={i18n._(t`Edit Template`)}
|
||||||
|
variant="plain"
|
||||||
|
component={Link}
|
||||||
|
to={`/templates/${template.type}/${template.id}/edit`}
|
||||||
|
>
|
||||||
|
<PencilAltIcon />
|
||||||
|
</Button>
|
||||||
|
</ActionItem>
|
||||||
|
<ActionItem
|
||||||
|
visible={template.summary_fields.user_capabilities.copy}
|
||||||
|
tooltip={i18n._(t`Copy Template`)}
|
||||||
|
>
|
||||||
|
<CopyButton
|
||||||
|
helperText={{
|
||||||
|
errorMessage: i18n._(t`Failed to copy template.`),
|
||||||
|
}}
|
||||||
|
isDisabled={isDisabled}
|
||||||
|
onCopyStart={handleCopyStart}
|
||||||
|
onCopyFinish={handleCopyFinish}
|
||||||
|
copyItem={copyTemplate}
|
||||||
|
/>
|
||||||
|
</ActionItem>
|
||||||
|
</ActionsTd>
|
||||||
|
</Tr>
|
||||||
|
<Tr isExpanded={isExpanded}>
|
||||||
|
<Td colspan={2} />
|
||||||
|
<Td colspan={4}>
|
||||||
|
<ExpandableRowContent>
|
||||||
|
<DetailList>
|
||||||
|
<Detail
|
||||||
|
label={i18n._(t`Activity`)}
|
||||||
|
value={<Sparkline jobs={summaryFields.recent_jobs} />}
|
||||||
|
/>
|
||||||
|
{summaryFields.credentials && summaryFields.credentials.length && (
|
||||||
|
<Detail
|
||||||
|
label={i18n._(t`Credentials`)}
|
||||||
|
value={
|
||||||
|
<ChipGroup
|
||||||
|
numChips={5}
|
||||||
|
totalChips={summaryFields.credentials.length}
|
||||||
|
>
|
||||||
|
{summaryFields.credentials.map(c => (
|
||||||
|
<CredentialChip key={c.id} credential={c} isReadOnly />
|
||||||
|
))}
|
||||||
|
</ChipGroup>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{summaryFields.inventory ? (
|
||||||
|
<Detail
|
||||||
|
label={i18n._(t`Inventory`)}
|
||||||
|
value={inventoryValue(
|
||||||
|
summaryFields.inventory.kind,
|
||||||
|
summaryFields.inventory.id
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
!askInventoryOnLaunch && (
|
||||||
|
<DeletedDetail label={i18n._(t`Inventory`)} />
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
{summaryFields.labels && summaryFields.labels.results.length > 0 && (
|
||||||
|
<Detail
|
||||||
|
label={i18n._(t`Labels`)}
|
||||||
|
value={
|
||||||
|
<ChipGroup
|
||||||
|
numChips={5}
|
||||||
|
totalChips={summaryFields.labels.results.length}
|
||||||
|
>
|
||||||
|
{summaryFields.labels.results.map(l => (
|
||||||
|
<Chip key={l.id} isReadOnly>
|
||||||
|
{l.name}
|
||||||
|
</Chip>
|
||||||
|
))}
|
||||||
|
</ChipGroup>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{summaryFields.project ? (
|
||||||
|
<Detail
|
||||||
|
label={i18n._(t`Project`)}
|
||||||
|
value={
|
||||||
|
<Link to={`/projects/${summaryFields.project.id}/details`}>
|
||||||
|
{summaryFields.project.name}
|
||||||
|
</Link>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<DeletedDetail label={i18n._(t`Project`)} />
|
||||||
|
)}
|
||||||
|
</DetailList>
|
||||||
|
</ExpandableRowContent>
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user