diff --git a/.byebug_history b/.byebug_history new file mode 100644 index 00000000000..a75e30eb481 --- /dev/null +++ b/.byebug_history @@ -0,0 +1,28 @@ +continue +cve.content_view.label +cve.environment.label +cve +continue +::Katello::ContentViewEnvironment.where(:content_view_id => @cv4.id, :environment_id => @dev.id).first +@cv4 = katello_content_views(:library_dev_view) +::Katello::ContentViewEnvironment.where(:content_view_id => @cv4.id, :environment_id => @dev.id).first +@cv4 = content_view_environments(:library_dev_view) +::Katello::ContentViewEnvironment.where(:content_view_id => @cv4.id, :environment_id => @dev.id).first +::Katello::ContentViewEnvironment.where(:content_view_id => @cv3.id, :environment_id => @dev.id).first +::Katello::ContentViewEnvironment.where(:content_view_id => @cv2.id, :environment_id => @dev.id).first +@cv3 +@cv2 +continue +@cv2 = katello_content_views(:library_view_no_version) +models +@dev +@cv2 +::Katello::ContentViewEnvironment.where(:content_view_id => @cv2.id, :environment_id => @dev.id).first +::Katello::ContentViewEnvironment.where(:content_view_id => @cv2.id, + 93: :environment_id => @dev.id).first +target_cvs_ids +next +continue +host.content_facet.content_view_environments.reload.ids +host.content_facet.content_view_environments.ids +host.content_facet.content_view_environments diff --git a/webpack/assets/javascripts/react_app/components/HostsIndex/ActionKebab.js b/webpack/assets/javascripts/react_app/components/HostsIndex/ActionKebab.js index 0af1524b467..ecb3fee62fb 100644 --- a/webpack/assets/javascripts/react_app/components/HostsIndex/ActionKebab.js +++ b/webpack/assets/javascripts/react_app/components/HostsIndex/ActionKebab.js @@ -1,6 +1,8 @@ -import React, { useState } from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; -import { Dropdown, KebabToggle } from '@patternfly/react-core'; +import { Menu, MenuToggle } from '@patternfly/react-core'; +import EllipsisVIcon from '@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon'; +import './ActionKebab.scss'; /** * Generate a button or a dropdown of buttons @@ -8,24 +10,27 @@ import { Dropdown, KebabToggle } from '@patternfly/react-core'; * @param {Object} action action to preform when the button is click can be href with data-method or Onclick * @return {Function} button component or splitbutton component */ -export const ActionKebab = ({ items }) => { - const [isOpen, setIsOpen] = useState(false); +export const ActionKebab = ({ items, menuOpen, setMenuOpen }) => { if (!items.length) return null; return ( <> - {items.length > 0 && ( - - } - isOpen={isOpen} - isPlain - dropdownItems={items} - /> + setMenuOpen(prev => !prev)} + isExpanded={menuOpen} + > + + + {items.length > 0 && menuOpen && ( + setMenuOpen(false)} + > + {items} + )} ); @@ -33,6 +38,8 @@ export const ActionKebab = ({ items }) => { ActionKebab.propTypes = { items: PropTypes.arrayOf(PropTypes.node), + menuOpen: PropTypes.bool.isRequired, + setMenuOpen: PropTypes.func.isRequired, }; ActionKebab.defaultProps = { diff --git a/webpack/assets/javascripts/react_app/components/HostsIndex/ActionKebab.scss b/webpack/assets/javascripts/react_app/components/HostsIndex/ActionKebab.scss new file mode 100644 index 00000000000..dba64bb3b2a --- /dev/null +++ b/webpack/assets/javascripts/react_app/components/HostsIndex/ActionKebab.scss @@ -0,0 +1,4 @@ +#hosts-index-actions-kebab { + position: absolute; + top: 3.1em; +} diff --git a/webpack/assets/javascripts/react_app/components/HostsIndex/TableRowActions/core.js b/webpack/assets/javascripts/react_app/components/HostsIndex/TableRowActions/core.js new file mode 100644 index 00000000000..8085c282c62 --- /dev/null +++ b/webpack/assets/javascripts/react_app/components/HostsIndex/TableRowActions/core.js @@ -0,0 +1,45 @@ +import forceSingleton from '../../../common/forceSingleton'; + +const coreTableRowActionsRegistry = forceSingleton( + 'coreTableRowActionsRegistry', + () => ({}) +); + +// Unlike the column registry which is collecting objects that describe table columns, +// here we collect an object containing a single getActions funtion, which returns an array of kebab action items. +export const registerGetActions = ({ + pluginName, + getActionsFunc, + tableName = 'hosts', +}) => { + if (!coreTableRowActionsRegistry[pluginName]) + coreTableRowActionsRegistry[pluginName] = {}; + coreTableRowActionsRegistry[pluginName][tableName] = { + getActions: getActionsFunc, + }; +}; + +export const registeredTableRowActions = ({ tableName = 'hosts' }) => { + const result = {}; + Object.keys(coreTableRowActionsRegistry).forEach(pluginName => { + if (coreTableRowActionsRegistry[pluginName]?.[tableName]) { + result[pluginName] = coreTableRowActionsRegistry[pluginName][tableName]; + } + }); + // { katello: { getActions: [Function: getActions] } } + return result; +}; + +export const getActions = (hostDetailsResult, { tableName = 'hosts' } = {}) => { + const result = []; + const allGetActionsFuncs = registeredTableRowActions({ tableName }); + Object.values(allGetActionsFuncs).forEach( + ({ getActions: getActionsFunc }) => { + if (typeof getActionsFunc !== 'function') return; + result.push(...getActionsFunc(hostDetailsResult)); + } + ); + return result; +}; + +export default getActions; diff --git a/webpack/assets/javascripts/react_app/components/HostsIndex/index.js b/webpack/assets/javascripts/react_app/components/HostsIndex/index.js index ab45dbda913..ea721b2c8d8 100644 --- a/webpack/assets/javascripts/react_app/components/HostsIndex/index.js +++ b/webpack/assets/javascripts/react_app/components/HostsIndex/index.js @@ -4,8 +4,10 @@ import { useSelector, useDispatch, shallowEqual } from 'react-redux'; import { Tr, Td, ActionsColumn } from '@patternfly/react-table'; import { ToolbarItem, + Divider, Dropdown, DropdownItem, + MenuItem, KebabToggle, Flex, FlexItem, @@ -52,6 +54,7 @@ import getColumnData from './Columns/core'; import { categoriesFromFrontendColumnData } from '../ColumnSelector/helpers'; import ColumnSelector from '../ColumnSelector'; import { ForemanActionsBarContext } from '../HostDetails/ActionsBar'; +import { registerGetActions, getActions } from './TableRowActions/core'; export const ForemanHostsIndexActionsBarContext = forceSingleton( 'ForemanHostsIndexActionsBarContext', @@ -59,6 +62,7 @@ export const ForemanHostsIndexActionsBarContext = forceSingleton( ); const HostsIndex = () => { + const [menuOpen, setMenuOpen] = useState(false); const [allColumns, setAllColumns] = useState( getColumnData({ tableName: 'hosts' }) ); @@ -205,48 +209,75 @@ const HostsIndex = () => { }); const dropdownItems = [ - - {__('Delete')} - , - {__('Build management')} - , - , + {__('Change host group')} - , + , + ]; + + const dangerZoneItems = [ + , + + {__('Delete')} + , ]; const registeredItems = useSelector(selectKebabItems, shallowEqual); const pluginToolbarItems = jsReady => ( - + {jsReady && } ); - const rowKebabItems = ({ + const coreRowKebabItems = ({ id, name: hostName, compute_id: computeId, can_delete: canDelete, + can_edit: canEdit, }) => [ + { + title: __('Edit'), + onClick: () => { + window.location.href = foremanUrl(`/hosts/${id}/edit`); + }, + isDisabled: !canEdit, + }, + { + title: __('Clone'), + onClick: () => { + window.location.href = foremanUrl(`/hosts/${id}/clone`); + }, + isDisabled: !canEdit, + }, { title: __('Delete'), onClick: () => deleteHostHandler({ id, hostName, computeId }), @@ -254,6 +285,11 @@ const HostsIndex = () => { }, ]; + registerGetActions({ + pluginName: 'core', + getActionsFunc: coreRowKebabItems, + }); + const [legacyUIKebabOpen, setLegacyUIKebabOpen] = useState(false); const legacyUIKebab = ( { ouiaId="hosts-index-table" params={params} setParams={setParamsAndAPI} - getActions={rowKebabItems} + getActions={getActions} itemCount={subtotal} results={results} url={HOSTS_API_PATH} @@ -364,7 +400,7 @@ const HostsIndex = () => { isPending={status === STATUS.PENDING} > {results?.map((result, rowIndex) => { - const rowActions = rowKebabItems(result); + const rowActions = getActions(result); return ( {} diff --git a/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/Table/Table.js b/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/Table/Table.js index f93621b9808..da311e02960 100644 --- a/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/Table/Table.js +++ b/webpack/assets/javascripts/react_app/components/PF4/TableIndexPage/Table/Table.js @@ -76,14 +76,22 @@ export const Table = ({ setSelectedItem({ id, name }); setDeleteModalOpen(true); }; - const actions = ({ can_delete: canDelete, id, name, ...item }) => + const actions = ({ + can_delete: canDelete, + can_edit: canEdit, + id, + name, + ...item + }) => [ isDeleteable && { title: __('Delete'), onClick: () => onDeleteClick({ id, name }), isDisabled: !canDelete, }, - ...((getActions && getActions({ id, name, canDelete, ...item })) ?? []), + ...((getActions && + getActions({ id, name, canDelete, canEdit, ...item })) ?? + []), ].filter(Boolean); const RowSelectTd = rowSelectTd; return (