mirror of
https://github.com/ansible/awx.git
synced 2026-04-14 06:29:25 -02:30
Add expanded row section to project list
This commit is contained in:
@@ -170,7 +170,7 @@ function ProjectList({ i18n }) {
|
|||||||
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>{i18n._(t`Status`)}</HeaderCell>
|
<HeaderCell>{i18n._(t`Status`)}</HeaderCell>
|
||||||
<HeaderCell>{i18n._(t`Type`)}</HeaderCell>
|
<HeaderCell>{i18n._(t`Type`)}</HeaderCell>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import React, { Fragment, useState, useCallback } from 'react';
|
|||||||
import { string, bool, func } from 'prop-types';
|
import { string, bool, func } from 'prop-types';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { Button, Tooltip } from '@patternfly/react-core';
|
import { Button, Tooltip } 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 { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
@@ -15,6 +15,12 @@ import { ActionsTd, ActionItem } from '../../../components/PaginatedTable';
|
|||||||
import { formatDateString, timeOfDay } from '../../../util/dates';
|
import { formatDateString, timeOfDay } from '../../../util/dates';
|
||||||
import { ProjectsAPI } from '../../../api';
|
import { ProjectsAPI } from '../../../api';
|
||||||
import ClipboardCopyButton from '../../../components/ClipboardCopyButton';
|
import ClipboardCopyButton from '../../../components/ClipboardCopyButton';
|
||||||
|
import {
|
||||||
|
DetailList,
|
||||||
|
Detail,
|
||||||
|
DeletedDetail,
|
||||||
|
} from '../../../components/DetailList';
|
||||||
|
import ExecutionEnvironmentDetail from '../../../components/ExecutionEnvironmentDetail';
|
||||||
import StatusLabel from '../../../components/StatusLabel';
|
import StatusLabel from '../../../components/StatusLabel';
|
||||||
import { toTitleCase } from '../../../util/strings';
|
import { toTitleCase } from '../../../util/strings';
|
||||||
import CopyButton from '../../../components/CopyButton';
|
import CopyButton from '../../../components/CopyButton';
|
||||||
@@ -39,6 +45,7 @@ function ProjectListItem({
|
|||||||
rowIndex,
|
rowIndex,
|
||||||
i18n,
|
i18n,
|
||||||
}) {
|
}) {
|
||||||
|
const [isExpanded, setIsExpanded] = useState(false);
|
||||||
const [isDisabled, setIsDisabled] = useState(false);
|
const [isDisabled, setIsDisabled] = useState(false);
|
||||||
ProjectListItem.propTypes = {
|
ProjectListItem.propTypes = {
|
||||||
project: Project.isRequired,
|
project: Project.isRequired,
|
||||||
@@ -87,104 +94,159 @@ function ProjectListItem({
|
|||||||
project.custom_virtualenv && !project.default_environment;
|
project.custom_virtualenv && !project.default_environment;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tr id={`${project.id}`}>
|
<>
|
||||||
<Td
|
<Tr id={`${project.id}`}>
|
||||||
select={{
|
<Td
|
||||||
rowIndex,
|
expand={{
|
||||||
isSelected,
|
rowIndex,
|
||||||
onSelect,
|
isExpanded,
|
||||||
}}
|
onToggle: () => setIsExpanded(!isExpanded),
|
||||||
dataLabel={i18n._(t`Selected`)}
|
}}
|
||||||
/>
|
|
||||||
<Td id={labelId} dataLabel={i18n._(t`Name`)}>
|
|
||||||
<span>
|
|
||||||
<Link to={`${detailUrl}`}>
|
|
||||||
<b>{project.name}</b>
|
|
||||||
</Link>
|
|
||||||
</span>
|
|
||||||
{missingExecutionEnvironment && (
|
|
||||||
<span>
|
|
||||||
<Tooltip
|
|
||||||
content={i18n._(
|
|
||||||
t`Custom virtual environment ${project.custom_virtualenv} must be replaced by an execution environment.`
|
|
||||||
)}
|
|
||||||
position="right"
|
|
||||||
className="missing-execution-environment"
|
|
||||||
>
|
|
||||||
<ExclamationTriangleIcon />
|
|
||||||
</Tooltip>
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</Td>
|
|
||||||
<Td dataLabel={i18n._(t`Status`)}>
|
|
||||||
{project.summary_fields.last_job && (
|
|
||||||
<Tooltip
|
|
||||||
position="top"
|
|
||||||
content={generateLastJobTooltip(project.summary_fields.last_job)}
|
|
||||||
key={project.summary_fields.last_job.id}
|
|
||||||
>
|
|
||||||
<Link to={`/jobs/project/${project.summary_fields.last_job.id}`}>
|
|
||||||
<StatusLabel status={project.summary_fields.last_job.status} />
|
|
||||||
</Link>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
</Td>
|
|
||||||
<Td dataLabel={i18n._(t`Type`)}>
|
|
||||||
{project.scm_type === ''
|
|
||||||
? i18n._(t`Manual`)
|
|
||||||
: toTitleCase(project.scm_type)}
|
|
||||||
</Td>
|
|
||||||
<Td dataLabel={i18n._(t`Revision`)}>
|
|
||||||
{project.scm_revision.substring(0, 7)}
|
|
||||||
{!project.scm_revision && (
|
|
||||||
<Label aria-label={i18n._(t`copy to clipboard disabled`)}>
|
|
||||||
{i18n._(t`Sync for revision`)}
|
|
||||||
</Label>
|
|
||||||
)}
|
|
||||||
<ClipboardCopyButton
|
|
||||||
isDisabled={!project.scm_revision}
|
|
||||||
stringToCopy={project.scm_revision}
|
|
||||||
copyTip={i18n._(t`Copy full revision to clipboard.`)}
|
|
||||||
copiedSuccessTip={i18n._(t`Successfully copied to clipboard!`)}
|
|
||||||
ouiaId="copy-revision-button"
|
|
||||||
/>
|
/>
|
||||||
</Td>
|
<Td
|
||||||
<ActionsTd dataLabel={i18n._(t`Actions`)}>
|
select={{
|
||||||
<ActionItem
|
rowIndex,
|
||||||
visible={project.summary_fields.user_capabilities.start}
|
isSelected,
|
||||||
tooltip={i18n._(t`Sync Project`)}
|
onSelect,
|
||||||
>
|
}}
|
||||||
<ProjectSyncButton projectId={project.id} />
|
dataLabel={i18n._(t`Selected`)}
|
||||||
</ActionItem>
|
/>
|
||||||
<ActionItem
|
<Td id={labelId} dataLabel={i18n._(t`Name`)}>
|
||||||
visible={project.summary_fields.user_capabilities.edit}
|
<span>
|
||||||
tooltip={i18n._(t`Edit Project`)}
|
<Link to={`${detailUrl}`}>
|
||||||
>
|
<b>{project.name}</b>
|
||||||
<Button
|
</Link>
|
||||||
ouiaId={`${project.id}-edit-button`}
|
</span>
|
||||||
isDisabled={isDisabled}
|
{missingExecutionEnvironment && (
|
||||||
aria-label={i18n._(t`Edit Project`)}
|
<span>
|
||||||
variant="plain"
|
<Tooltip
|
||||||
component={Link}
|
content={i18n._(
|
||||||
to={`/projects/${project.id}/edit`}
|
t`Custom virtual environment ${project.custom_virtualenv} must be replaced by an execution environment.`
|
||||||
>
|
)}
|
||||||
<PencilAltIcon />
|
position="right"
|
||||||
</Button>
|
className="missing-execution-environment"
|
||||||
</ActionItem>
|
>
|
||||||
<ActionItem
|
<ExclamationTriangleIcon />
|
||||||
tooltip={i18n._(t`Copy Project`)}
|
</Tooltip>
|
||||||
visible={project.summary_fields.user_capabilities.copy}
|
</span>
|
||||||
>
|
)}
|
||||||
<CopyButton
|
</Td>
|
||||||
copyItem={copyProject}
|
<Td dataLabel={i18n._(t`Status`)}>
|
||||||
isDisabled={isDisabled}
|
{project.summary_fields.last_job && (
|
||||||
onCopyStart={handleCopyStart}
|
<Tooltip
|
||||||
onCopyFinish={handleCopyFinish}
|
position="top"
|
||||||
errorMessage={i18n._(t`Failed to copy project.`)}
|
content={generateLastJobTooltip(project.summary_fields.last_job)}
|
||||||
|
key={project.summary_fields.last_job.id}
|
||||||
|
>
|
||||||
|
<Link to={`/jobs/project/${project.summary_fields.last_job.id}`}>
|
||||||
|
<StatusLabel status={project.summary_fields.last_job.status} />
|
||||||
|
</Link>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</Td>
|
||||||
|
<Td dataLabel={i18n._(t`Type`)}>
|
||||||
|
{project.scm_type === ''
|
||||||
|
? i18n._(t`Manual`)
|
||||||
|
: toTitleCase(project.scm_type)}
|
||||||
|
</Td>
|
||||||
|
<Td dataLabel={i18n._(t`Revision`)}>
|
||||||
|
{project.scm_revision.substring(0, 7)}
|
||||||
|
{!project.scm_revision && (
|
||||||
|
<Label aria-label={i18n._(t`copy to clipboard disabled`)}>
|
||||||
|
{i18n._(t`Sync for revision`)}
|
||||||
|
</Label>
|
||||||
|
)}
|
||||||
|
<ClipboardCopyButton
|
||||||
|
isDisabled={!project.scm_revision}
|
||||||
|
stringToCopy={project.scm_revision}
|
||||||
|
copyTip={i18n._(t`Copy full revision to clipboard.`)}
|
||||||
|
copiedSuccessTip={i18n._(t`Successfully copied to clipboard!`)}
|
||||||
|
ouiaId="copy-revision-button"
|
||||||
/>
|
/>
|
||||||
</ActionItem>
|
</Td>
|
||||||
</ActionsTd>
|
<ActionsTd dataLabel={i18n._(t`Actions`)}>
|
||||||
</Tr>
|
<ActionItem
|
||||||
|
visible={project.summary_fields.user_capabilities.start}
|
||||||
|
tooltip={i18n._(t`Sync Project`)}
|
||||||
|
>
|
||||||
|
<ProjectSyncButton projectId={project.id} />
|
||||||
|
</ActionItem>
|
||||||
|
<ActionItem
|
||||||
|
visible={project.summary_fields.user_capabilities.edit}
|
||||||
|
tooltip={i18n._(t`Edit Project`)}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
ouiaId={`${project.id}-edit-button`}
|
||||||
|
isDisabled={isDisabled}
|
||||||
|
aria-label={i18n._(t`Edit Project`)}
|
||||||
|
variant="plain"
|
||||||
|
component={Link}
|
||||||
|
to={`/projects/${project.id}/edit`}
|
||||||
|
>
|
||||||
|
<PencilAltIcon />
|
||||||
|
</Button>
|
||||||
|
</ActionItem>
|
||||||
|
<ActionItem
|
||||||
|
tooltip={i18n._(t`Copy Project`)}
|
||||||
|
visible={project.summary_fields.user_capabilities.copy}
|
||||||
|
>
|
||||||
|
<CopyButton
|
||||||
|
copyItem={copyProject}
|
||||||
|
isDisabled={isDisabled}
|
||||||
|
onCopyStart={handleCopyStart}
|
||||||
|
onCopyFinish={handleCopyFinish}
|
||||||
|
errorMessage={i18n._(t`Failed to copy project.`)}
|
||||||
|
/>
|
||||||
|
</ActionItem>
|
||||||
|
</ActionsTd>
|
||||||
|
</Tr>
|
||||||
|
<Tr isExpanded={isExpanded} id={`expanded-project-row-${project.id}`}>
|
||||||
|
<Td colSpan={2} />
|
||||||
|
<Td colSpan={5}>
|
||||||
|
<ExpandableRowContent>
|
||||||
|
<DetailList>
|
||||||
|
<Detail
|
||||||
|
label={i18n._(t`Description`)}
|
||||||
|
value={project.description}
|
||||||
|
dataCy={`project-${project.id}-description`}
|
||||||
|
/>
|
||||||
|
{project.summary_fields.organization ? (
|
||||||
|
<Detail
|
||||||
|
label={i18n._(t`Organization`)}
|
||||||
|
value={
|
||||||
|
<Link
|
||||||
|
to={`/organizations/${project.summary_fields.organization.id}/details`}
|
||||||
|
>
|
||||||
|
{project.summary_fields.organization.name}
|
||||||
|
</Link>
|
||||||
|
}
|
||||||
|
dataCy={`project-${project.id}-organization`}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<DeletedDetail label={i18n._(t`Organization`)} />
|
||||||
|
)}
|
||||||
|
<ExecutionEnvironmentDetail
|
||||||
|
virtualEnvironment={project.custom_virtualenv}
|
||||||
|
executionEnvironment={
|
||||||
|
project.summary_fields?.default_environment
|
||||||
|
}
|
||||||
|
isDefaultEnvironment
|
||||||
|
/>
|
||||||
|
<Detail
|
||||||
|
label={i18n._(t`Last modified`)}
|
||||||
|
value={formatDateString(project.modified)}
|
||||||
|
dataCy={`project-${project.id}-last-modified`}
|
||||||
|
/>
|
||||||
|
<Detail
|
||||||
|
label={i18n._(t`Last used`)}
|
||||||
|
value={formatDateString(project.last_job_run)}
|
||||||
|
dataCy={`project-${project.id}-last-used`}
|
||||||
|
/>
|
||||||
|
</DetailList>
|
||||||
|
</ExpandableRowContent>
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
export default withI18n()(ProjectListItem);
|
export default withI18n()(ProjectListItem);
|
||||||
|
|||||||
Reference in New Issue
Block a user