- [X] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. ## Testing - [X] Added/updated automated tests - [X] QA'd all new/changed functionality manually <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Query responses now include pack details only when the requester has permission to view them. * Prevented pack metadata from being disclosed across fleets when query names overlap. * Corrected target selection labels and empty-state messaging for fleet-based targets. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
162 lines
4.3 KiB
React
162 lines
4.3 KiB
React
import React from "react";
|
|
import PropTypes from "prop-types";
|
|
import classNames from "classnames";
|
|
import { filter, includes, isEqual, noop } from "lodash";
|
|
|
|
import targetInterface from "interfaces/target";
|
|
import EmptyState from "components/EmptyState";
|
|
import TargetDetails from "../TargetDetails";
|
|
import { targetFilter } from "./helpers";
|
|
import TargetOption from "../TargetOption";
|
|
|
|
const baseClass = "target-list";
|
|
|
|
const SelectTargetsMenuWrapper = (
|
|
onMoreInfoClick,
|
|
moreInfoTarget,
|
|
handleBackToResults,
|
|
isPremiumTier
|
|
) => {
|
|
const SelectTargetsMenu = ({
|
|
focusedOption,
|
|
instancePrefix,
|
|
onFocus,
|
|
onSelect,
|
|
optionClassName,
|
|
optionComponent,
|
|
options,
|
|
valueArray = [],
|
|
valueKey,
|
|
onOptionRef,
|
|
}) => {
|
|
const Option = optionComponent;
|
|
|
|
const renderTargets = (targetType) => {
|
|
const targets = filter(options, targetFilter(targetType));
|
|
const targetsOutput = [];
|
|
let targetTitle = targetType;
|
|
if (targetType === "all") {
|
|
targetTitle = "all hosts";
|
|
} else if (targetType === "teams") {
|
|
targetTitle = "fleets";
|
|
}
|
|
|
|
targetsOutput.push(
|
|
<p className={`${baseClass}__type`} key={`type-${targetType}-key`}>
|
|
{targetTitle}
|
|
</p>
|
|
);
|
|
|
|
if (targets.length === 0) {
|
|
if (targetType === "all") {
|
|
return false;
|
|
}
|
|
|
|
targetsOutput.push(
|
|
<span
|
|
className={`${baseClass}__not-found`}
|
|
key={`${targetType}-notfound`}
|
|
>
|
|
Unable to find any matching {targetTitle}.
|
|
</span>
|
|
);
|
|
|
|
return targetsOutput;
|
|
}
|
|
|
|
targetsOutput.push(
|
|
targets.map((target, index) => {
|
|
const { disabled: isDisabled } = target;
|
|
const isSelected = includes(valueArray, target);
|
|
const isFocused = isEqual(focusedOption, target);
|
|
const className = classNames(optionClassName, {
|
|
"Select-option": true,
|
|
"is-selected": isSelected,
|
|
"is-focused": isFocused,
|
|
"is-disabled": true,
|
|
});
|
|
const setRef = (ref) => {
|
|
onOptionRef(ref, isFocused);
|
|
};
|
|
|
|
return (
|
|
<Option
|
|
className={className}
|
|
instancePrefix={instancePrefix}
|
|
isDisabled={isDisabled}
|
|
isFocused={isFocused}
|
|
isSelected={isSelected}
|
|
key={`option-${target[valueKey]}-${target.id}`}
|
|
onFocus={onFocus}
|
|
onSelect={noop}
|
|
option={target}
|
|
optionIndex={index}
|
|
ref={setRef}
|
|
>
|
|
<TargetOption
|
|
target={target}
|
|
onSelect={onSelect}
|
|
onMoreInfoClick={onMoreInfoClick}
|
|
/>
|
|
</Option>
|
|
);
|
|
})
|
|
);
|
|
|
|
return targetsOutput;
|
|
};
|
|
|
|
const hasHostTargets = () => {
|
|
return options.find((option) => option.count !== 0) !== undefined;
|
|
};
|
|
|
|
const renderTargetGroups = (
|
|
<>
|
|
{renderTargets("all")}
|
|
{isPremiumTier && renderTargets("teams")}
|
|
{renderTargets("labels")}
|
|
{renderTargets("hosts")}
|
|
</>
|
|
);
|
|
|
|
return (
|
|
<div className={baseClass}>
|
|
<div className={`${baseClass}__options`}>
|
|
{hasHostTargets() ? (
|
|
renderTargetGroups
|
|
) : (
|
|
<EmptyState
|
|
header="You have no hosts to run this report against"
|
|
info="Expecting to see hosts? Try again in a few seconds as the system catches up."
|
|
/>
|
|
)}
|
|
</div>
|
|
<div className={`${baseClass}__option-details`}>
|
|
<TargetDetails
|
|
target={moreInfoTarget}
|
|
className={`${baseClass}__spotlight`}
|
|
handleBackToResults={handleBackToResults}
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
SelectTargetsMenu.propTypes = {
|
|
focusedOption: targetInterface,
|
|
instancePrefix: PropTypes.string,
|
|
onFocus: PropTypes.func,
|
|
onSelect: PropTypes.func,
|
|
optionClassName: PropTypes.string,
|
|
optionComponent: PropTypes.node,
|
|
options: PropTypes.arrayOf(targetInterface),
|
|
valueArray: PropTypes.arrayOf(targetInterface),
|
|
valueKey: PropTypes.string,
|
|
onOptionRef: PropTypes.func,
|
|
};
|
|
|
|
return SelectTargetsMenu;
|
|
};
|
|
|
|
export default SelectTargetsMenuWrapper;
|