Skip to content

Commit

Permalink
Fixes #36576 - Add Activation Key details top bar
Browse files Browse the repository at this point in the history
  • Loading branch information
Trevor Allison committed Jul 7, 2023
1 parent 2fdde82 commit 2f723ae
Show file tree
Hide file tree
Showing 7 changed files with 494 additions and 2 deletions.
30 changes: 30 additions & 0 deletions webpack/scenes/ActivationKeys/Details/ActivationKeyActions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { translate as __ } from 'foremanReact/common/I18n';
import { APIActions, API_OPERATIONS, put, get } from 'foremanReact/redux/API';
import { errorToast } from '../../Tasks/helpers';
import api from '../../../services/api';
import { ACTIVATION_KEY } from './ActivationKeyConstants';

export const getActivationKey = akId => get({
type: API_OPERATIONS.GET,
key: `${ACTIVATION_KEY}_${akId}`,
url: api.getApiUrl(`/activation_keys/${akId}`),
});

export const putActivationKey = (akId, params) => put({
type: API_OPERATIONS.PUT,
key: `${ACTIVATION_KEY}_${akId}`,
url: api.getApiUrl(`/activation_keys/${akId}`),
successToast: () => __('Activation key details updated'),
errorToast,
params,
});

export const deleteActivationKey = akId => APIActions.delete({
type: API_OPERATIONS.DELETE,
key: `${ACTIVATION_KEY}_${akId}`,
url: api.getApiUrl(`/activation_keys/${akId}`),
successToast: () => __('Activation key deleted'),
errorToast,
});

export default getActivationKey;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const ACTIVATION_KEY = 'ACTIVATION_KEY';

export default ACTIVATION_KEY;
106 changes: 104 additions & 2 deletions webpack/scenes/ActivationKeys/Details/ActivationKeyDetails.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,112 @@
import React from 'react';
import React, {
useEffect,
useState,
} from 'react';
import {
useDispatch,
useSelector,
} from 'react-redux';
import PropTypes from 'prop-types';
import { propsToCamelCase } from 'foremanReact/common/helpers';
import { selectAPIResponse } from 'foremanReact/redux/API/APISelectors';
import {
Title,
TextContent,
Text,
TextVariants,
Breadcrumb,
BreadcrumbItem,
Grid,
GridItem,
Label,
Split,
SplitItem,
Flex,
FlexItem,
Panel,
} from '@patternfly/react-core';
import './ActivationKeyDetails.scss';
import EditModal from './components/EditModal';
import DeleteMenu from './components/DeleteMenu';
import { getActivationKey } from './ActivationKeyActions';
import DeleteModal from './components/DeleteModal';

const ActivationKeyDetails = ({ match }) => <div>ActivationKeyDetails { match?.params?.id } </div>;

const ActivationKeyDetails = ({ match }) => {
const dispatch = useDispatch();
const akId = match?.params?.id;
const akDetailsResponse = useSelector(state => selectAPIResponse(state, `ACTIVATION_KEY_${akId}`));
const akDetails = propsToCamelCase(akDetailsResponse);
useEffect(() => {
if (akId) { // TODO add back akNotLoaded condition
dispatch(getActivationKey(akId));
}
}, [akId, dispatch]);

const [isModalOpen, setModalOpen] = useState(false);
const handleModalToggle = () => {
setModalOpen(!isModalOpen);
};

return (
<div >
<Panel className="ak-details-header">
<div className="breadcrumb-bar-pf4">
<Breadcrumb ouiaId="ak-breadcrumbs" className="breadcrumb-display">
<BreadcrumbItem className="breadcrumb-list" to="/activation_keys">Activation keys</BreadcrumbItem>
<BreadcrumbItem to="#" isActive>
{akDetails.name}
</BreadcrumbItem>
</Breadcrumb>
</div>
<Grid>
<GridItem span={8} className="ak-name-wrapper">
<Flex justifyContent={{ default: 'jusifyContentSpaceBetween' }} alignItems={{ default: 'alignItemsCenter' }}>
<FlexItem>
<Title ouiaId="ak-title" headingLevel="h5" size="2xl" className="ak-name-truncate">
{akDetails.name}
</Title>
</FlexItem>
<FlexItem>
<Split hasGutter style={{ display: 'inline-flex' }}>
<SplitItem>
<Label>
{akDetails.usageCount}/{akDetails.unlimitedHosts ? 'Unlimited' : akDetails.maxHosts}
</Label>
</SplitItem>
</Split>
</FlexItem>
</Flex>
</GridItem>
<GridItem offset={8} span={4}>
<Flex>
<FlexItem align={{ default: 'align-right' }}>
<Split>
<SplitItem>
<EditModal akDetails={akDetails} akId={akId} />
</SplitItem>
<DeleteMenu handleModalToggle={handleModalToggle} akId={akId} />
</Split>
</FlexItem>
</Flex>
</GridItem>
</Grid>
<div className="ak-details-description">
<TextContent>
<Text ouiaId="ak-description" component={TextVariants.p}>
{akDetails.description ? akDetails.description : 'Description empty'}
</Text>
</TextContent>
</div>
</Panel>
<DeleteModal isModalOpen={isModalOpen} handleModalToggle={handleModalToggle} akId={akId} />
</div>
);
};

export default ActivationKeyDetails;


ActivationKeyDetails.propTypes = {
match: PropTypes.shape({
params: PropTypes.shape({
Expand Down
37 changes: 37 additions & 0 deletions webpack/scenes/ActivationKeys/Details/ActivationKeyDetails.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
.ak-details-header {
margin: 0 24px 16px;
padding-top: 16px;
}

.ak-details-description {
padding-top: 16px;
}

.breadcrumb-bar-pf4 {
margin: 0 0 16px;
}

.breadcrumb-display {
display: block;
}

.breadcrumb-list {
display: flex;
flex-wrap: wrap;
align-items: center;
}

.ak-name-truncate {
text-overflow: ellipsis;
max-width: 100%;
white-space: nowrap;
overflow: hidden;
display: inline-block;
margin-right: 16px
}

.ak-name-wrapper {
display: inline-flex;
max-width: 60%;
margin-right: 8px;
}
61 changes: 61 additions & 0 deletions webpack/scenes/ActivationKeys/Details/components/DeleteMenu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Dropdown, DropdownItem, KebabToggle, DropdownPosition } from '@patternfly/react-core';
import { noop } from 'foremanReact/common/helpers';

const DeleteMenu = ({ handleModalToggle, akId }) => {
const [isOpen, setIsOpen] = React.useState(false);
const onToggle = (isOpenValue) => {
setIsOpen(isOpenValue);
};
const onFocus = () => {
const element = document.getElementById('toggle-kebab');
element.focus();
};
const onSelect = () => {
setIsOpen(false);
onFocus();
};
const dropdownItems = [
<DropdownItem
ouiaId="delete-menu-option"
key="delete"
component="button"
onClick={handleModalToggle}
>
Delete
</DropdownItem>,
<DropdownItem
ouiaId="linkbacktooldpage"
key="link"
href={`../../../activation_keys/${akId}`}
>
Old Activation key Details Page
</DropdownItem>];
return (
<React.Fragment>
<Dropdown
ouiaId="dekete-action"
onSelect={onSelect}
position={DropdownPosition.right}
toggle={<KebabToggle id="toggle-kebab" onToggle={onToggle} />}
isOpen={isOpen}
isPlain
dropdownItems={dropdownItems}
/>
</React.Fragment>
);
};

DeleteMenu.propTypes = {
handleModalToggle: PropTypes.func,
akId: PropTypes.string,
};

DeleteMenu.defaultProps = {
handleModalToggle: noop,
akId: '',
};

export default DeleteMenu;

65 changes: 65 additions & 0 deletions webpack/scenes/ActivationKeys/Details/components/DeleteModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React from 'react';
import {
useDispatch,
} from 'react-redux';
import PropTypes from 'prop-types';
import { noop } from 'foremanReact/common/helpers';
import { Modal, ModalVariant, Button, Icon, Title, Flex } from '@patternfly/react-core';
import ExclamationTriangleIcon from '@patternfly/react-icons/dist/esm/icons/exclamation-triangle-icon';
import { deleteActivationKey } from '../ActivationKeyActions';

const DeleteModal = ({ isModalOpen, handleModalToggle, akId }) => {
const dispatch = useDispatch();

const handleDelete = () => {
dispatch(deleteActivationKey(akId));
handleModalToggle();
window.location.replace('/activation_keys');
};

return (
<React.Fragment>
<Modal
ouiaId="ak-delete-modal"
variant={ModalVariant.small}
title={[
<Flex>
<Icon status="warning">
<ExclamationTriangleIcon />
</Icon>
<Title ouiaId="ak-delete-header" headingLevel="h5" size="2xl">
Delete activation key?
</Title>
</Flex>,
]}
isOpen={isModalOpen}
onClose={handleModalToggle}
actions={[
<Button ouiaId="delete-button" key="delete" variant="danger" onClick={handleDelete}>
Delete
</Button>,
<Button ouiaId="cancel-button" key="cancel" variant="link" onClick={handleModalToggle}>
Cancel
</Button>,
]}
>
Activation Key will no longer be available for use. This operation cannot be undone.
</Modal>
</React.Fragment>
);
};


DeleteModal.propTypes = {
isModalOpen: PropTypes.bool,
handleModalToggle: PropTypes.func,
akId: PropTypes.string,
};

DeleteModal.defaultProps = {
isModalOpen: false,
handleModalToggle: noop,
akId: '',
};

export default DeleteModal;
Loading

0 comments on commit 2f723ae

Please sign in to comment.