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 && (
+
)}
>
);
@@ -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')}
- ,
- ,
+ ,
+ ,
+ ];
+
+ const dangerZoneItems = [
+ ,
+ ,
];
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 (