1
0
mirror of https://github.com/ansible/awx.git synced 2024-10-31 23:51:09 +03:00

Adds copy button to JTList

This commit is contained in:
Alex Corey 2020-04-22 09:16:00 -04:00
parent 72de660ea1
commit 8d31d09d4a
5 changed files with 114 additions and 14 deletions

View File

@ -19,6 +19,10 @@ class JobTemplates extends SchedulesMixin(
this.readWebhookKey = this.readWebhookKey.bind(this);
}
copyTemplate(id, data) {
return this.http.post(`${this.baseUrl}${id}/copy/`, data);
}
launch(id, data) {
return this.http.post(`${this.baseUrl}${id}/launch/`, data);
}

View File

@ -0,0 +1,54 @@
import React, { useCallback, useEffect } from 'react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { Button, Tooltip } from '@patternfly/react-core';
import { CopyIcon } from '@patternfly/react-icons';
import useRequest, { useDismissableError } from '@util/useRequest';
import AlertModal from '@components/AlertModal';
import ErrorDetail from '@components/ErrorDetail';
function CopyButton({ i18n, itemName, copyItem, disableButtons }) {
const { isLoading, error, request: copyTemplateToAPI } = useRequest(
useCallback(async () => {
await copyItem();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []),
{}
);
useEffect(() => {
if (isLoading) {
return disableButtons(true);
}
return disableButtons(false);
}, [isLoading, disableButtons]);
const { dismissError } = useDismissableError(error);
return (
<>
<Tooltip content={i18n._(t`Copy Template`)} position="top">
<Button
aria-label={i18n._(t`Copy Template`)}
css="grid-column: 3"
variant="plain"
onClick={() => copyTemplateToAPI()}
>
<CopyIcon />
</Button>
</Tooltip>
<AlertModal
isOpen={error}
variant="error"
title={i18n._(t`Error!`)}
onClose={() => dismissError}
>
{i18n._(t`Failed to copy ${itemName}.`)}
<ErrorDetail error={error} />
</AlertModal>
</>
);
}
export default withI18n()(CopyButton);

View File

@ -227,6 +227,7 @@ function TemplateList({ i18n }) {
detailUrl={`/templates/${template.type}/${template.id}`}
onSelect={() => handleSelect(template)}
isSelected={selected.some(row => row.id === template.id)}
fetchTemplates={fetchTemplates}
/>
)}
emptyStateControls={(canAddJT || canAddWFJT) && addButton}

View File

@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import {
Button,
@ -18,11 +18,14 @@ import {
PencilAltIcon,
RocketIcon,
} from '@patternfly/react-icons';
import { timeOfDay } from '@util/dates';
import { JobTemplatesAPI } from '@api';
import LaunchButton from '@components/LaunchButton';
import Sparkline from '@components/Sparkline';
import { toTitleCase } from '@util/strings';
import styled from 'styled-components';
import CopyButton from './CopyButton';
const DataListAction = styled(_DataListAction)`
align-items: center;
@ -31,7 +34,15 @@ const DataListAction = styled(_DataListAction)`
grid-template-columns: repeat(2, 40px);
`;
function TemplateListItem({ i18n, template, isSelected, onSelect, detailUrl }) {
function TemplateListItem({
i18n,
template,
isSelected,
onSelect,
detailUrl,
fetchTemplates,
}) {
const [disableButtons, setDisableButtons] = useState(false);
const labelId = `check-action-${template.id}`;
const canLaunch = template.summary_fields.user_capabilities.start;
@ -40,11 +51,11 @@ function TemplateListItem({ i18n, template, isSelected, onSelect, detailUrl }) {
(!template.summary_fields.project ||
(!template.summary_fields.inventory &&
!template.ask_inventory_on_launch));
return (
<DataListItem aria-labelledby={labelId} id={`${template.id}`}>
<DataListItemRow>
<DataListCheck
isDisabled={disableButtons}
id={`select-jobTemplate-${template.id}`}
checked={isSelected}
onChange={onSelect}
@ -89,6 +100,7 @@ function TemplateListItem({ i18n, template, isSelected, onSelect, detailUrl }) {
<LaunchButton resource={template}>
{({ handleLaunch }) => (
<Button
isDisabled={disableButtons}
aria-label={i18n._(t`Launch template`)}
css="grid-column: 1"
variant="plain"
@ -101,17 +113,34 @@ function TemplateListItem({ i18n, template, isSelected, onSelect, detailUrl }) {
</Tooltip>
)}
{template.summary_fields.user_capabilities.edit ? (
<Tooltip content={i18n._(t`Edit Template`)} position="top">
<Button
aria-label={i18n._(t`Edit Template`)}
css="grid-column: 2"
variant="plain"
component={Link}
to={`/templates/${template.type}/${template.id}/edit`}
>
<PencilAltIcon />
</Button>
</Tooltip>
<>
<Tooltip content={i18n._(t`Edit Template`)} position="top">
<Button
isDisabled={disableButtons}
aria-label={i18n._(t`Edit Template`)}
css="grid-column: 2"
variant="plain"
component={Link}
to={`/templates/${template.type}/${template.id}/edit`}
>
<PencilAltIcon />
</Button>
</Tooltip>
{template.summary_fields.user_capabilities.copy && (
<CopyButton
isDisabled={disableButtons}
css="grid-column: 3"
itemName={template.name}
disableButtons={setDisableButtons}
copyItem={async () => {
await JobTemplatesAPI.copyTemplate(template.id, {
name: `${template.name}@${timeOfDay()}`,
});
await fetchTemplates();
}}
/>
)}
</>
) : (
''
)}

View File

@ -17,6 +17,18 @@ export function secondsToHHMMSS(seconds) {
return new Date(seconds * 1000).toISOString().substr(11, 8);
}
export function timeOfDay() {
const date = new Date();
const hour = date.getHours();
const minute = prependZeros(date.getMinutes());
const second = prependZeros(date.getSeconds());
const time =
hour > 12
? `${hour - 12}:${minute} :${second} PM`
: `${hour}:${minute}:${second}`;
return time;
}
export function dateToInputDateTime(dateObj) {
// input type="date-time" expects values to be formatted
// like: YYYY-MM-DDTHH-MM-SS