From 0082ac5081256044a19f2a214021555fb20358f1 Mon Sep 17 00:00:00 2001 From: Trevor Allison Date: Thu, 29 Jun 2023 15:42:43 +0000 Subject: [PATCH] Fixes #36610 - Add system purpose to activation key details reset master remove stuff --- .../SystemPurposeCard/SystemPurposeActions.js | 32 +++- .../SystemPurposeCard/SystemPurposeCard.js | 167 +++++++++++------- .../SystemPurposeConstants.js | 2 + .../SystemPurposeEditModal.js | 71 +++++--- 4 files changed, 178 insertions(+), 94 deletions(-) diff --git a/webpack/components/extensions/HostDetails/Cards/SystemPurposeCard/SystemPurposeActions.js b/webpack/components/extensions/HostDetails/Cards/SystemPurposeCard/SystemPurposeActions.js index b3765f5ba13..197a554dcd8 100644 --- a/webpack/components/extensions/HostDetails/Cards/SystemPurposeCard/SystemPurposeActions.js +++ b/webpack/components/extensions/HostDetails/Cards/SystemPurposeCard/SystemPurposeActions.js @@ -2,7 +2,8 @@ import { translate as __ } from 'foremanReact/common/I18n'; import { API_OPERATIONS, put } from 'foremanReact/redux/API'; import api, { foremanApi } from '../../../../../services/api'; import HOST_DETAILS_KEY from '../../HostDetailsConstants'; -import { ORGANIZATION, AVAILABLE_RELEASE_VERSIONS } from './SystemPurposeConstants'; +import { ACTIVATION_KEY } from '../../../../../scenes/ActivationKeys/Details/ActivationKeyConstants'; +import { ORGANIZATION, AVAILABLE_RELEASE_VERSIONS, RELEASES } from './SystemPurposeConstants'; import { errorToast } from '../../../../../scenes/Tasks/helpers'; export const getOrganization = ({ orgId }) => ({ @@ -13,7 +14,7 @@ export const getOrganization = ({ orgId }) => ({ }, }); -export const getAvailableReleaseVersions = ({ hostId }) => ({ +export const getHostAvailableReleaseVersions = ({ hostId }) => ({ type: 'API_GET', payload: { key: `${AVAILABLE_RELEASE_VERSIONS}_${hostId}`, @@ -21,12 +22,20 @@ export const getAvailableReleaseVersions = ({ hostId }) => ({ }, }); -export const updateSystemPurposeAttributes = ({ hostId, attributes, refreshHostDetails }) => put({ +export const getAKAvailableReleaseVersions = ({ id }) => ({ + type: API_OPERATIONS.GET, + payload: { + key: `${RELEASES}_${id}`, + url: api.getApiUrl(`/activation_keys/${id}/releases`), + }, +}); + +export const updateHostSysPurposeAttributes = ({ id, attributes, refreshHostDetails }) => put({ type: API_OPERATIONS.PUT, key: HOST_DETAILS_KEY, - url: foremanApi.getApiUrl(`/hosts/${hostId}`), + url: foremanApi.getApiUrl(`/hosts/${id}`), params: { - id: hostId, + id, host: { subscription_facet_attributes: attributes, }, @@ -35,3 +44,16 @@ export const updateSystemPurposeAttributes = ({ hostId, attributes, refreshHostD errorToast, handleSuccess: refreshHostDetails, }); + +export const updateAKSysPurposeAttributes = ({ id, attributes, refreshAKDetails }) => put({ + type: API_OPERATIONS.PUT, + key: ACTIVATION_KEY, + url: api.getApiUrl(`/activation_keys/${id}`), + params: { + id, + activation_key: attributes, + }, + successToast: () => __('System purpose attributes updated'), + errorToast, + handleSuccess: refreshAKDetails, +}); diff --git a/webpack/components/extensions/HostDetails/Cards/SystemPurposeCard/SystemPurposeCard.js b/webpack/components/extensions/HostDetails/Cards/SystemPurposeCard/SystemPurposeCard.js index 86fb9258a8b..676eb4a7576 100644 --- a/webpack/components/extensions/HostDetails/Cards/SystemPurposeCard/SystemPurposeCard.js +++ b/webpack/components/extensions/HostDetails/Cards/SystemPurposeCard/SystemPurposeCard.js @@ -2,6 +2,7 @@ import React, { useState } from 'react'; import { useSelector } from 'react-redux'; import PropTypes from 'prop-types'; import { STATUS } from 'foremanReact/constants'; +import { selectAPIStatus } from 'foremanReact/redux/API/APISelectors'; import { Button, Card, @@ -20,6 +21,7 @@ import { ListItem, Tooltip, Skeleton, + CardExpandableContent, } from '@patternfly/react-core'; import { OutlinedQuestionCircleIcon } from '@patternfly/react-icons'; import { translate as __ } from 'foremanReact/common/I18n'; @@ -29,13 +31,16 @@ import SystemPurposeEditModal from './SystemPurposeEditModal'; import { selectHostDetailsStatus } from '../../HostDetailsSelectors'; import { hasRequiredPermissions, hostIsNotRegistered } from '../../hostDetailsHelpers'; -const SystemPurposeCard = ({ hostDetails }) => { - const showEditButton = hasRequiredPermissions(['edit_hosts'], hostDetails?.permissions); - const { organization_id: orgId, name: hostName } = hostDetails; - const subscriptionFacetAttributes = hostDetails?.subscription_facet_attributes; +const SystemPurposeCard = ({ hostDetails, akDetails }) => { + const details = hostDetails?.id ? hostDetails : akDetails; + const sysPurposeCardType = details?.subscription_facet_attributes ? 'host' : 'ak'; + const requiredPermission = sysPurposeCardType === 'host' ? 'edit_hosts' : 'edit_activation_keys'; + const showEditButton = hasRequiredPermissions([requiredPermission], details?.permissions); + const { organization_id: orgId, name: hostName } = details; + const subscriptionFacetAttributes = details?.subscription_facet_attributes; const { purposeRole, purposeUsage, purposeAddons, releaseVersion, serviceLevel, - } = propsToCamelCase(subscriptionFacetAttributes ?? {}); + } = propsToCamelCase((subscriptionFacetAttributes || details) ?? {}); const sysPurposeProps = { purposeRole, purposeUsage, @@ -43,11 +48,30 @@ const SystemPurposeCard = ({ hostDetails }) => { releaseVersion, serviceLevel, }; - const hostDetailsStatus = useSelector(selectHostDetailsStatus); - const dataIsLoading = hostDetailsStatus === STATUS.PENDING; + + const selectAKDetailsStatus = state => + selectAPIStatus(state, `ACTIVATION_KEY_${details.id}`) ?? STATUS.PENDING; + + const statusSelector = sysPurposeCardType === 'host' ? selectHostDetailsStatus : selectAKDetailsStatus; + const detailsStatus = useSelector(statusSelector); + const dataIsLoading = detailsStatus === STATUS.PENDING; const [editing, setEditing] = useState(false); - if (!hostDetails?.id) { + + const [isExpanded, setIsExpanded] = React.useState(false); + + const onExpand = () => { + setIsExpanded(!isExpanded); + }; + + const cardHeaderProps = { + toggleButtonProps: { id: 'sys-purpose-toggle', 'aria-label': 'sys-purpose-toggle' }, + }; + if (sysPurposeCardType === 'ak') { + cardHeaderProps.onExpand = onExpand; + } + + if (!details?.id) { return ( @@ -57,12 +81,12 @@ const SystemPurposeCard = ({ hostDetails }) => { ); } - if (hostIsNotRegistered({ hostDetails })) return null; + if (sysPurposeCardType === 'host' && hostIsNotRegistered({ hostDetails: details })) return null; return ( - - + + { } - - - - {__('Role')} - - {dataIsLoading ? : purposeRole} - - {__('SLA')} - - {serviceLevel && (dataIsLoading ? : ( - - ))} - - {__('Usage type')} - - {purposeUsage && (dataIsLoading ? : ( - - ))} - - {__('Release version')} - - {dataIsLoading ? : releaseVersion} - - {!!purposeAddons?.length && ( - <> - {__('Add-ons')} - {dataIsLoading ? : ( - - - {purposeAddons.map(addon => ( - {addon} - ))} - - - )} - - ) - } - - - {showEditButton && ( - setEditing(false)} - hostName={hostName} - hostId={hostDetails.id} - {...sysPurposeProps} - /> - )} - + + + + + {__('Role')} + + {dataIsLoading ? : purposeRole} + + {__('SLA')} + + {serviceLevel && (dataIsLoading ? : ( + + ))} + + {__('Usage type')} + + {purposeUsage && (dataIsLoading ? : ( + + ))} + + {__('Release version')} + + {dataIsLoading ? : releaseVersion} + + {!!purposeAddons?.length && ( + <> + {__('Add-ons')} + {dataIsLoading ? : ( + + + {purposeAddons.map(addon => ( + {addon} + ))} + + + )} + + ) + } + + + {showEditButton && ( + setEditing(false)} + name={hostName} + id={details.id} + {...sysPurposeProps} + type={sysPurposeCardType} + /> + )} + + ); @@ -167,10 +194,24 @@ SystemPurposeCard.propTypes = { edit_hosts: PropTypes.bool, }), }), + akDetails: PropTypes.shape({ + name: PropTypes.string, + organization_id: PropTypes.number, + id: PropTypes.number, + purpose_usage: PropTypes.string, + purpose_role: PropTypes.string, + release_version: PropTypes.string, + service_level: PropTypes.string, + purpose_addons: PropTypes.arrayOf(PropTypes.string), + permissions: PropTypes.shape({ + edit_activation_keys: PropTypes.bool, + }), + }), }; SystemPurposeCard.defaultProps = { hostDetails: {}, + akDetails: {}, }; export default SystemPurposeCard; diff --git a/webpack/components/extensions/HostDetails/Cards/SystemPurposeCard/SystemPurposeConstants.js b/webpack/components/extensions/HostDetails/Cards/SystemPurposeCard/SystemPurposeConstants.js index 7090b90baf9..a79167664ef 100644 --- a/webpack/components/extensions/HostDetails/Cards/SystemPurposeCard/SystemPurposeConstants.js +++ b/webpack/components/extensions/HostDetails/Cards/SystemPurposeCard/SystemPurposeConstants.js @@ -4,3 +4,5 @@ export const defaultServiceLevels = ['Self-Support', 'Standard', 'Premium']; export const ORGANIZATION = 'ORGANIZATION'; export const AVAILABLE_RELEASE_VERSIONS = 'AVAILABLE_RELEASE_VERSIONS'; + +export const RELEASES = 'RELEASES'; diff --git a/webpack/components/extensions/HostDetails/Cards/SystemPurposeCard/SystemPurposeEditModal.js b/webpack/components/extensions/HostDetails/Cards/SystemPurposeCard/SystemPurposeEditModal.js index 8d747aa0d03..d60aab0d577 100644 --- a/webpack/components/extensions/HostDetails/Cards/SystemPurposeCard/SystemPurposeEditModal.js +++ b/webpack/components/extensions/HostDetails/Cards/SystemPurposeCard/SystemPurposeEditModal.js @@ -18,13 +18,14 @@ import { import { FormattedMessage } from 'react-intl'; import { translate as __ } from 'foremanReact/common/I18n'; import { selectOrganizationStatus, selectOrganization, selectAvailableReleaseVersions, selectAvailableReleaseVersionsStatus } from './SystemPurposeSelectors'; -import { getAvailableReleaseVersions, getOrganization, updateSystemPurposeAttributes } from './SystemPurposeActions'; +import { getHostAvailableReleaseVersions, getAKAvailableReleaseVersions, getOrganization, updateHostSysPurposeAttributes, updateAKSysPurposeAttributes } from './SystemPurposeActions'; import HOST_DETAILS_KEY from '../../HostDetailsConstants'; import { defaultUsages, defaultRoles, defaultServiceLevels } from './SystemPurposeConstants'; +import { getActivationKey } from '../../../../../scenes/ActivationKeys/Details/ActivationKeyActions'; const SystemPurposeEditModal = ({ - closeModal, hostName, purposeRole, purposeUsage, purposeAddons, - serviceLevel, releaseVersion, isOpen, orgId, hostId, + closeModal, name, purposeRole, purposeUsage, purposeAddons, + serviceLevel, releaseVersion, isOpen, orgId, id, type, }) => { const initialPurposeRole = purposeRole ?? ''; const initialServiceLevel = serviceLevel ?? ''; @@ -58,7 +59,7 @@ const SystemPurposeEditModal = ({ const availableReleaseVersionsStatus = useSelector(state => selectAvailableReleaseVersionsStatus(state, orgId)); const availableReleaseVersions = useSelector(state => - selectAvailableReleaseVersions(state, hostId))?.results ?? []; + selectAvailableReleaseVersions(state, id))?.results ?? []; useEffect(() => { if (orgId && orgStatus !== STATUS.RESOLVED) { dispatch(getOrganization({ orgId })); @@ -66,10 +67,12 @@ const SystemPurposeEditModal = ({ }, [orgId, orgStatus, dispatch]); useEffect(() => { - if (hostId && availableReleaseVersionsStatus !== STATUS.RESOLVED) { - dispatch(getAvailableReleaseVersions({ hostId })); + if (type === 'host' && id && availableReleaseVersionsStatus !== STATUS.RESOLVED) { + dispatch(getHostAvailableReleaseVersions({ id })); + } else if (type === 'ak' && id) { + dispatch(getAKAvailableReleaseVersions({ id })); } - }, [hostId, availableReleaseVersionsStatus, dispatch]); + }, [type, id, availableReleaseVersionsStatus, dispatch]); const toggleAddonSelect = isOpenState => setAddonSelectOpen(isOpenState); @@ -88,7 +91,7 @@ const SystemPurposeEditModal = ({ type: 'API_GET', payload: { key: HOST_DETAILS_KEY, - url: `/api/hosts/${hostName}`, + url: `/api/hosts/${name}`, }, }); @@ -133,18 +136,33 @@ const SystemPurposeEditModal = ({ closeModal(); const optionsToValue = (options, stateValue) => options.find(option => option.value === stateValue)?.value; - dispatch(updateSystemPurposeAttributes({ - hostId, - attributes: { - autoheal: true, - purpose_role: optionsToValue(roleOptions, selectedRole), - purpose_usage: optionsToValue(usageOptions, selectedUsage), - purpose_addons: selectedAddons, - release_version: optionsToValue(releaseVersionOptions, selectedReleaseVersion), - service_level: optionsToValue(serviceLevelOptions, selectedServiceLevel), - }, - refreshHostDetails, - })); + if (type === 'host') { + dispatch(updateHostSysPurposeAttributes({ + id, + attributes: { + autoheal: true, + purpose_role: optionsToValue(roleOptions, selectedRole), + purpose_usage: optionsToValue(usageOptions, selectedUsage), + purpose_addons: selectedAddons, + release_version: optionsToValue(releaseVersionOptions, selectedReleaseVersion), + service_level: optionsToValue(serviceLevelOptions, selectedServiceLevel), + }, + refreshHostDetails, + })); + } else { + dispatch(updateAKSysPurposeAttributes({ + id, + attributes: { + autoheal: true, + purpose_role: optionsToValue(roleOptions, selectedRole), + purpose_usage: optionsToValue(usageOptions, selectedUsage), + purpose_addons: selectedAddons, + release_version: optionsToValue(releaseVersionOptions, selectedReleaseVersion), + service_level: optionsToValue(serviceLevelOptions, selectedServiceLevel), + }, + refreshAKDetails: () => dispatch(getActivationKey(id)), + })); + } }; const handleCancel = () => { @@ -180,9 +198,9 @@ const SystemPurposeEditModal = ({ {hostName}, + name: {name}, }} />
@@ -288,7 +306,7 @@ export default SystemPurposeEditModal; SystemPurposeEditModal.propTypes = { closeModal: PropTypes.func.isRequired, - hostName: PropTypes.string, + name: PropTypes.string, purposeRole: PropTypes.string.isRequired, purposeUsage: PropTypes.string.isRequired, purposeAddons: PropTypes.arrayOf(PropTypes.string).isRequired, @@ -296,12 +314,13 @@ SystemPurposeEditModal.propTypes = { releaseVersion: PropTypes.string, isOpen: PropTypes.bool.isRequired, orgId: PropTypes.number, - hostId: PropTypes.number, + id: PropTypes.number, + type: PropTypes.string.isRequired, }; SystemPurposeEditModal.defaultProps = { - hostName: '', + name: '', orgId: null, - hostId: null, + id: null, releaseVersion: '', };