mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 08:55:24 +00:00
Fleet UI: Fix bug with label sidebar not collapsing (#7402)
This commit is contained in:
parent
4930616bbb
commit
3e310ba150
1
changes/issue-7364-fix-label-collapsing-sidebar
Normal file
1
changes/issue-7364-fix-label-collapsing-sidebar
Normal file
@ -0,0 +1 @@
|
||||
* Bug fix to collapse label sidebar
|
32
frontend/hooks/useToggleSidePanel.ts
Normal file
32
frontend/hooks/useToggleSidePanel.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
interface IUseToggleSidePanelHook {
|
||||
isSidePanelOpen: boolean;
|
||||
toggleSidePanel: () => void;
|
||||
setSidePanelOpen: (isOpen: boolean) => void;
|
||||
}
|
||||
|
||||
const useToggleSidePanel = (
|
||||
initialIsOpened: boolean
|
||||
): IUseToggleSidePanelHook => {
|
||||
const [isSidePanelOpen, setIsOpen] = useState<boolean>(initialIsOpened);
|
||||
|
||||
const toggleSidePanel = useCallback(() => {
|
||||
setIsOpen(!isSidePanelOpen);
|
||||
}, [setIsOpen, isSidePanelOpen]);
|
||||
|
||||
const setSidePanelOpen = useCallback(
|
||||
(isOpen: boolean) => {
|
||||
setIsOpen(isOpen);
|
||||
},
|
||||
[setIsOpen]
|
||||
);
|
||||
|
||||
return {
|
||||
isSidePanelOpen,
|
||||
toggleSidePanel,
|
||||
setSidePanelOpen,
|
||||
};
|
||||
};
|
||||
|
||||
export default useToggleSidePanel;
|
@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { useState, useContext, useEffect } from "react";
|
||||
import { IAceEditor } from "react-ace/lib/types";
|
||||
import { noop, size } from "lodash";
|
||||
import { useDebouncedCallback } from "use-debounce";
|
||||
@ -12,16 +12,19 @@ import InputField from "components/forms/fields/InputField";
|
||||
import FleetAce from "components/FleetAce";
|
||||
// @ts-ignore
|
||||
import validateQuery from "components/forms/validators/validate_query";
|
||||
import InfoIcon from "../../../../assets/images/icon-info-purple-14x14@2x.png";
|
||||
|
||||
interface ILabelFormProps {
|
||||
baseError: string;
|
||||
selectedLabel?: ILabel;
|
||||
isEdit?: boolean;
|
||||
isUpdatingLabel?: boolean;
|
||||
onCancel: () => void;
|
||||
handleSubmit: (formData: ILabelFormData) => void;
|
||||
onOsqueryTableSelect?: (tableName: string) => void;
|
||||
onOpenSchemaSidebar: () => void;
|
||||
onOsqueryTableSelect: (tableName: string) => void;
|
||||
showOpenSchemaActionText: boolean;
|
||||
backendValidators: { [key: string]: string };
|
||||
isUpdatingLabel?: boolean;
|
||||
}
|
||||
|
||||
const baseClass = "label-form";
|
||||
@ -57,11 +60,13 @@ const LabelForm = ({
|
||||
baseError,
|
||||
selectedLabel,
|
||||
isEdit,
|
||||
isUpdatingLabel,
|
||||
onCancel,
|
||||
handleSubmit,
|
||||
onOpenSchemaSidebar,
|
||||
onOsqueryTableSelect,
|
||||
showOpenSchemaActionText,
|
||||
backendValidators,
|
||||
isUpdatingLabel,
|
||||
}: ILabelFormProps): JSX.Element => {
|
||||
const [name, setName] = useState(selectedLabel?.name || "");
|
||||
const [nameError, setNameError] = useState("");
|
||||
@ -156,6 +161,21 @@ const LabelForm = ({
|
||||
});
|
||||
};
|
||||
|
||||
const renderLabelComponent = (): JSX.Element | null => {
|
||||
if (!showOpenSchemaActionText) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Button variant="text-icon" onClick={onOpenSchemaSidebar}>
|
||||
<>
|
||||
<img alt="" src={InfoIcon} />
|
||||
Show schema
|
||||
</>
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
const isBuiltin =
|
||||
selectedLabel &&
|
||||
(selectedLabel.label_type === "builtin" || selectedLabel.type === "status");
|
||||
@ -190,6 +210,7 @@ const LabelForm = ({
|
||||
onChange={onQueryChange}
|
||||
value={query}
|
||||
label="SQL"
|
||||
labelActionComponent={renderLabelComponent()}
|
||||
onLoad={onLoad}
|
||||
readOnly={isEdit}
|
||||
wrapperClassName={`${baseClass}__text-editor-wrapper`}
|
218
frontend/pages/LabelPage/LabelPage.tsx
Normal file
218
frontend/pages/LabelPage/LabelPage.tsx
Normal file
@ -0,0 +1,218 @@
|
||||
import React, { useState, useContext, useEffect } from "react";
|
||||
import { useQuery } from "react-query";
|
||||
import { InjectedRouter, Params } from "react-router/lib/Router";
|
||||
|
||||
import PATHS from "router/paths";
|
||||
import MainContent from "components/MainContent";
|
||||
import SidePanelContent from "components/SidePanelContent";
|
||||
import QuerySidePanel from "components/side_panels/QuerySidePanel";
|
||||
import Spinner from "components/Spinner";
|
||||
import { QueryContext } from "context/query";
|
||||
import { NotificationContext } from "context/notification";
|
||||
import { IApiError } from "interfaces/errors";
|
||||
import { ILabel, ILabelFormData } from "interfaces/label";
|
||||
import labelsAPI, { ILabelsResponse } from "services/entities/labels";
|
||||
import deepDifference from "utilities/deep_difference";
|
||||
import useToggleSidePanel from "hooks/useToggleSidePanel";
|
||||
|
||||
import LabelForm from "./LabelForm";
|
||||
|
||||
const baseClass = "label-page";
|
||||
|
||||
interface ILabelPageProps {
|
||||
router: InjectedRouter;
|
||||
params: Params;
|
||||
location: {
|
||||
pathname: string;
|
||||
};
|
||||
}
|
||||
|
||||
const DEFAULT_CREATE_LABEL_ERRORS = {
|
||||
name: "",
|
||||
};
|
||||
|
||||
const LabelPage = ({
|
||||
router,
|
||||
params,
|
||||
location,
|
||||
}: ILabelPageProps): JSX.Element | null => {
|
||||
const isEditLabel = !location.pathname.includes("new");
|
||||
|
||||
const [selectedLabel, setSelectedLabel] = useState<ILabel>();
|
||||
const { isSidePanelOpen, setSidePanelOpen } = useToggleSidePanel(true);
|
||||
const [showOpenSchemaActionText, setShowOpenSchemaActionText] = useState(
|
||||
false
|
||||
);
|
||||
const [labelValidator, setLabelValidator] = useState<{
|
||||
[key: string]: string;
|
||||
}>(DEFAULT_CREATE_LABEL_ERRORS);
|
||||
const [isUpdatingLabel, setIsUpdatingLabel] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
const { selectedOsqueryTable, setSelectedOsqueryTable } = useContext(
|
||||
QueryContext
|
||||
);
|
||||
const { renderFlash } = useContext(NotificationContext);
|
||||
|
||||
const { data: labels, error: labelsError } = useQuery<
|
||||
ILabelsResponse,
|
||||
Error,
|
||||
ILabel[]
|
||||
>(["labels"], () => labelsAPI.loadAll(), {
|
||||
select: (data: ILabelsResponse) => data.labels,
|
||||
onSuccess: (responseLabels: ILabel[]) => {
|
||||
if (params.label_id) {
|
||||
const selectLabel = responseLabels.find(
|
||||
(label) => label.id === parseInt(params.label_id, 10)
|
||||
);
|
||||
setSelectedLabel(selectLabel);
|
||||
setIsLoading(false);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const onCloseSchemaSidebar = () => {
|
||||
setSidePanelOpen(false);
|
||||
setShowOpenSchemaActionText(true);
|
||||
};
|
||||
|
||||
const onOpenSchemaSidebar = () => {
|
||||
setSidePanelOpen(true);
|
||||
setShowOpenSchemaActionText(false);
|
||||
};
|
||||
|
||||
const onOsqueryTableSelect = (tableName: string) => {
|
||||
setSelectedOsqueryTable(tableName);
|
||||
};
|
||||
|
||||
const onEditLabel = (formData: ILabelFormData) => {
|
||||
if (!selectedLabel) {
|
||||
console.error("Label isn't available. This should not happen.");
|
||||
return;
|
||||
}
|
||||
|
||||
setIsUpdatingLabel(true);
|
||||
const updateAttrs = deepDifference(formData, selectedLabel);
|
||||
|
||||
labelsAPI
|
||||
.update(selectedLabel, updateAttrs)
|
||||
.then(() => {
|
||||
router.push(PATHS.MANAGE_HOSTS_LABEL(selectedLabel.id));
|
||||
renderFlash(
|
||||
"success",
|
||||
"Label updated. Try refreshing this page in just a moment to see the updated host count for your label."
|
||||
);
|
||||
})
|
||||
.catch((updateError: { data: IApiError }) => {
|
||||
if (updateError.data.errors[0].reason.includes("Duplicate")) {
|
||||
setLabelValidator({
|
||||
name: "A label with this name already exists",
|
||||
});
|
||||
} else if (
|
||||
updateError.data.errors[0].reason.includes(
|
||||
"Data too long for column 'name'"
|
||||
)
|
||||
) {
|
||||
setLabelValidator({
|
||||
name: "Label name is too long",
|
||||
});
|
||||
} else if (
|
||||
updateError.data.errors[0].reason.includes(
|
||||
"Data too long for column 'description'"
|
||||
)
|
||||
) {
|
||||
setLabelValidator({
|
||||
description: "Label description is too long",
|
||||
});
|
||||
} else {
|
||||
renderFlash("error", "Could not create label. Please try again.");
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
setIsUpdatingLabel(false);
|
||||
});
|
||||
};
|
||||
|
||||
const onAddLabel = (formData: ILabelFormData) => {
|
||||
setIsUpdatingLabel(true);
|
||||
|
||||
labelsAPI
|
||||
.create(formData)
|
||||
.then((label: ILabel) => {
|
||||
router.push(PATHS.MANAGE_HOSTS_LABEL(label.id));
|
||||
renderFlash(
|
||||
"success",
|
||||
"Label created. Try refreshing this page in just a moment to see the updated host count for your label."
|
||||
);
|
||||
})
|
||||
.catch((updateError: any) => {
|
||||
if (updateError.data.errors[0].reason.includes("Duplicate")) {
|
||||
setLabelValidator({
|
||||
name: "A label with this name already exists",
|
||||
});
|
||||
} else if (
|
||||
updateError.data.errors[0].reason.includes(
|
||||
"Data too long for column 'name'"
|
||||
)
|
||||
) {
|
||||
setLabelValidator({
|
||||
name: "Label name is too long",
|
||||
});
|
||||
} else if (
|
||||
updateError.data.errors[0].reason.includes(
|
||||
"Data too long for column 'description'"
|
||||
)
|
||||
) {
|
||||
setLabelValidator({
|
||||
description: "Label description is too long",
|
||||
});
|
||||
} else {
|
||||
renderFlash("error", "Could not create label. Please try again.");
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
setIsUpdatingLabel(false);
|
||||
});
|
||||
};
|
||||
|
||||
const onCancelLabel = () => {
|
||||
router.goBack();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<MainContent className={baseClass}>
|
||||
<div className={`${baseClass}__wrapper`}>
|
||||
{isLoading ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
<LabelForm
|
||||
selectedLabel={selectedLabel}
|
||||
onCancel={onCancelLabel}
|
||||
isEdit={isEditLabel}
|
||||
isUpdatingLabel={isUpdatingLabel}
|
||||
handleSubmit={isEditLabel ? onEditLabel : onAddLabel}
|
||||
onOpenSchemaSidebar={onOpenSchemaSidebar}
|
||||
onOsqueryTableSelect={onOsqueryTableSelect}
|
||||
baseError={labelsError?.message || ""}
|
||||
backendValidators={labelValidator}
|
||||
showOpenSchemaActionText={showOpenSchemaActionText}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</MainContent>
|
||||
{isSidePanelOpen && !isEditLabel && (
|
||||
<SidePanelContent>
|
||||
<QuerySidePanel
|
||||
key="query-side-panel"
|
||||
onOsqueryTableSelect={onOsqueryTableSelect}
|
||||
selectedOsqueryTable={selectedOsqueryTable}
|
||||
onClose={onCloseSchemaSidebar}
|
||||
/>
|
||||
</SidePanelContent>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LabelPage;
|
14
frontend/pages/LabelPage/_styles.scss
Normal file
14
frontend/pages/LabelPage/_styles.scss
Normal file
@ -0,0 +1,14 @@
|
||||
.label-page {
|
||||
&__sandboxMode {
|
||||
margin-top: 70px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0 0 $pad-xlarge;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: $x-small;
|
||||
font-weight: $bold;
|
||||
}
|
||||
}
|
1
frontend/pages/LabelPage/index.ts
Normal file
1
frontend/pages/LabelPage/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default } from "./LabelPage";
|
@ -26,25 +26,21 @@ import {
|
||||
|
||||
import PATHS from "router/paths";
|
||||
import { AppContext } from "context/app";
|
||||
import { QueryContext } from "context/query";
|
||||
import { TableContext } from "context/table";
|
||||
import { NotificationContext } from "context/notification";
|
||||
import {
|
||||
IEnrollSecret,
|
||||
IEnrollSecretsResponse,
|
||||
} from "interfaces/enroll_secret";
|
||||
import { IApiError } from "interfaces/errors";
|
||||
import { IHost } from "interfaces/host";
|
||||
import { ILabel, ILabelFormData } from "interfaces/label";
|
||||
import { ILabel } from "interfaces/label";
|
||||
import { IMDMSolution } from "interfaces/macadmins";
|
||||
import { IOperatingSystemVersion } from "interfaces/operating_system";
|
||||
import { IPolicy } from "interfaces/policy";
|
||||
import { ISoftware } from "interfaces/software";
|
||||
import { ITeam } from "interfaces/team";
|
||||
import deepDifference from "utilities/deep_difference";
|
||||
import sortUtils from "utilities/sort";
|
||||
import {
|
||||
DEFAULT_CREATE_LABEL_ERRORS,
|
||||
HOSTS_SEARCH_BOX_PLACEHOLDER,
|
||||
HOSTS_SEARCH_BOX_TOOLTIP,
|
||||
PLATFORM_LABEL_DISPLAY_NAMES,
|
||||
@ -54,14 +50,12 @@ import {
|
||||
import Button from "components/buttons/Button";
|
||||
// @ts-ignore
|
||||
import Dropdown from "components/forms/fields/Dropdown";
|
||||
import QuerySidePanel from "components/side_panels/QuerySidePanel";
|
||||
import TableContainer from "components/TableContainer";
|
||||
import TableDataError from "components/DataError";
|
||||
import { IActionButtonProps } from "components/TableContainer/DataTable/ActionButton";
|
||||
import TeamsDropdown from "components/TeamsDropdown";
|
||||
import Spinner from "components/Spinner";
|
||||
import MainContent from "components/MainContent";
|
||||
import SidePanelContent from "components/SidePanelContent";
|
||||
|
||||
import { getValidatedTeamId } from "utilities/helpers";
|
||||
import {
|
||||
@ -70,8 +64,6 @@ import {
|
||||
generateAvailableTableHeaders,
|
||||
} from "./HostTableConfig";
|
||||
import {
|
||||
NEW_LABEL_HASH,
|
||||
EDIT_LABEL_HASH,
|
||||
ALL_HOSTS_LABEL,
|
||||
LABEL_SLUG_PREFIX,
|
||||
DEFAULT_SORT_HEADER,
|
||||
@ -80,8 +72,6 @@ import {
|
||||
HOST_SELECT_STATUSES,
|
||||
} from "./constants";
|
||||
import { isAcceptableStatus, getNextLocationPath } from "./helpers";
|
||||
|
||||
import LabelForm from "./components/LabelForm";
|
||||
import DeleteSecretModal from "../../../components/DeleteSecretModal";
|
||||
import SecretEditorModal from "../../../components/SecretEditorModal";
|
||||
import AddHostsModal from "../../../components/AddHostsModal";
|
||||
@ -166,10 +156,6 @@ const ManageHostsPage = ({
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const { selectedOsqueryTable, setSelectedOsqueryTable } = useContext(
|
||||
QueryContext
|
||||
);
|
||||
const { setResetSelectedRows } = useContext(TableContext);
|
||||
|
||||
const hostHiddenColumns = localStorage.getItem("hostHiddenColumns");
|
||||
@ -242,18 +228,13 @@ const ManageHostsPage = ({
|
||||
currentQueryOptions,
|
||||
setCurrentQueryOptions,
|
||||
] = useState<ILoadHostsOptions>();
|
||||
const [labelValidator, setLabelValidator] = useState<{
|
||||
[key: string]: string;
|
||||
}>(DEFAULT_CREATE_LABEL_ERRORS);
|
||||
const [resetPageIndex, setResetPageIndex] = useState(false);
|
||||
const [isUpdatingLabel, setIsUpdatingLabel] = useState(false);
|
||||
const [isUpdatingSecret, setIsUpdatingSecret] = useState(false);
|
||||
const [isUpdatingHosts, setIsUpdatingHosts] = useState(false);
|
||||
const [resetPageIndex, setResetPageIndex] = useState<boolean>(false);
|
||||
const [isUpdatingLabel, setIsUpdatingLabel] = useState<boolean>(false);
|
||||
const [isUpdatingSecret, setIsUpdatingSecret] = useState<boolean>(false);
|
||||
const [isUpdatingHosts, setIsUpdatingHosts] = useState<boolean>(false);
|
||||
|
||||
// ======== end states
|
||||
|
||||
const isAddLabel = location.hash === NEW_LABEL_HASH;
|
||||
const isEditLabel = location.hash === EDIT_LABEL_HASH;
|
||||
const routeTemplate = route?.path ?? "";
|
||||
const policyId = queryParams?.policy_id;
|
||||
const policyResponse: PolicyResponse = queryParams?.policy_response;
|
||||
@ -747,17 +728,12 @@ const ManageHostsPage = ({
|
||||
};
|
||||
|
||||
const onAddLabelClick = () => {
|
||||
setLabelValidator(DEFAULT_CREATE_LABEL_ERRORS);
|
||||
router.push(`${PATHS.MANAGE_HOSTS}${NEW_LABEL_HASH}`);
|
||||
router.push(`${PATHS.NEW_LABEL}`);
|
||||
};
|
||||
|
||||
const onEditLabelClick = (evt: React.MouseEvent<HTMLButtonElement>) => {
|
||||
evt.preventDefault();
|
||||
|
||||
setLabelValidator(DEFAULT_CREATE_LABEL_ERRORS);
|
||||
router.push(
|
||||
`${PATHS.MANAGE_HOSTS}/${getLabelSelected()}${EDIT_LABEL_HASH}`
|
||||
);
|
||||
router.push(`${PATHS.EDIT_LABEL(parseInt(labelID, 10))}`);
|
||||
};
|
||||
|
||||
const onSaveColumns = (newHiddenColumns: string[]) => {
|
||||
@ -766,10 +742,6 @@ const ManageHostsPage = ({
|
||||
setShowEditColumnsModal(false);
|
||||
};
|
||||
|
||||
const onCancelLabel = () => {
|
||||
router.goBack();
|
||||
};
|
||||
|
||||
// NOTE: used to reset page number to 0 when modifying filters
|
||||
useEffect(() => {
|
||||
setResetPageIndex(false);
|
||||
@ -985,102 +957,6 @@ const ManageHostsPage = ({
|
||||
}
|
||||
};
|
||||
|
||||
const onEditLabel = (formData: ILabelFormData) => {
|
||||
if (!selectedLabel) {
|
||||
console.error("Label isn't available. This should not happen.");
|
||||
return;
|
||||
}
|
||||
|
||||
const updateAttrs = deepDifference(formData, selectedLabel);
|
||||
setIsUpdatingLabel(true);
|
||||
|
||||
labelsAPI
|
||||
.update(selectedLabel, updateAttrs)
|
||||
.then(() => {
|
||||
refetchLabels();
|
||||
renderFlash(
|
||||
"success",
|
||||
"Label updated. Try refreshing this page in just a moment to see the updated host count for your label."
|
||||
);
|
||||
setLabelValidator({});
|
||||
})
|
||||
.catch((updateError: { data: IApiError }) => {
|
||||
if (updateError.data.errors[0].reason.includes("Duplicate")) {
|
||||
setLabelValidator({
|
||||
name: "A label with this name already exists",
|
||||
});
|
||||
} else if (
|
||||
updateError.data.errors[0].reason.includes(
|
||||
"Data too long for column 'name'"
|
||||
)
|
||||
) {
|
||||
setLabelValidator({
|
||||
name: "Label name is too long",
|
||||
});
|
||||
} else if (
|
||||
updateError.data.errors[0].reason.includes(
|
||||
"Data too long for column 'description'"
|
||||
)
|
||||
) {
|
||||
setLabelValidator({
|
||||
description: "Label description is too long",
|
||||
});
|
||||
} else {
|
||||
renderFlash("error", "Could not create label. Please try again.");
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
setIsUpdatingLabel(false);
|
||||
});
|
||||
};
|
||||
|
||||
const onOsqueryTableSelect = (tableName: string) => {
|
||||
setSelectedOsqueryTable(tableName);
|
||||
};
|
||||
|
||||
const onSaveAddLabel = (formData: ILabelFormData) => {
|
||||
setIsUpdatingLabel(true);
|
||||
labelsAPI
|
||||
.create(formData)
|
||||
.then(() => {
|
||||
router.push(PATHS.MANAGE_HOSTS);
|
||||
renderFlash(
|
||||
"success",
|
||||
"Label created. Try refreshing this page in just a moment to see the updated host count for your label."
|
||||
);
|
||||
setLabelValidator({});
|
||||
refetchLabels();
|
||||
})
|
||||
.catch((updateError: any) => {
|
||||
if (updateError.data.errors[0].reason.includes("Duplicate")) {
|
||||
setLabelValidator({
|
||||
name: "A label with this name already exists",
|
||||
});
|
||||
} else if (
|
||||
updateError.data.errors[0].reason.includes(
|
||||
"Data too long for column 'name'"
|
||||
)
|
||||
) {
|
||||
setLabelValidator({
|
||||
name: "Label name is too long",
|
||||
});
|
||||
} else if (
|
||||
updateError.data.errors[0].reason.includes(
|
||||
"Data too long for column 'description'"
|
||||
)
|
||||
) {
|
||||
setLabelValidator({
|
||||
description: "Label description is too long",
|
||||
});
|
||||
} else {
|
||||
renderFlash("error", "Could not create label. Please try again.");
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
setIsUpdatingLabel(false);
|
||||
});
|
||||
};
|
||||
|
||||
const onClearLabelFilter = () => {
|
||||
const allHostsLabel = labels?.find((label) => label.name === "All Hosts");
|
||||
if (allHostsLabel !== undefined) {
|
||||
@ -1724,38 +1600,6 @@ const ManageHostsPage = ({
|
||||
return null;
|
||||
};
|
||||
|
||||
const renderForm = () => {
|
||||
if (isAddLabel) {
|
||||
return (
|
||||
<LabelForm
|
||||
onCancel={onCancelLabel}
|
||||
onOsqueryTableSelect={onOsqueryTableSelect}
|
||||
handleSubmit={onSaveAddLabel}
|
||||
baseError={labelsError?.message || ""}
|
||||
backendValidators={labelValidator}
|
||||
isUpdatingLabel={isUpdatingLabel}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (isEditLabel) {
|
||||
return (
|
||||
<LabelForm
|
||||
selectedLabel={selectedLabel}
|
||||
onCancel={onCancelLabel}
|
||||
onOsqueryTableSelect={onOsqueryTableSelect}
|
||||
handleSubmit={onEditLabel}
|
||||
baseError={labelsError?.message || ""}
|
||||
backendValidators={labelValidator}
|
||||
isUpdatingLabel={isUpdatingLabel}
|
||||
isEdit
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const renderCustomControls = () => {
|
||||
// we filter out the status labels as we dont want to display them in the label
|
||||
// filter select dropdown.
|
||||
@ -1932,63 +1776,47 @@ const ManageHostsPage = ({
|
||||
return (
|
||||
<>
|
||||
<MainContent>
|
||||
<>
|
||||
{renderForm()}
|
||||
{!isAddLabel && !isEditLabel && (
|
||||
<div className={`${baseClass}`}>
|
||||
<div className="header-wrap">
|
||||
{renderHeader()}
|
||||
<div className={`${baseClass} button-wrap`}>
|
||||
{!isSandboxMode &&
|
||||
canEnrollHosts &&
|
||||
!hasHostErrors &&
|
||||
!hasHostCountErrors && (
|
||||
<Button
|
||||
onClick={() => setShowEnrollSecretModal(true)}
|
||||
className={`${baseClass}__enroll-hosts button`}
|
||||
variant="inverse"
|
||||
>
|
||||
<span>Manage enroll secret</span>
|
||||
</Button>
|
||||
)}
|
||||
{canEnrollHosts &&
|
||||
!hasHostErrors &&
|
||||
!hasHostCountErrors &&
|
||||
!(
|
||||
getStatusSelected() === ALL_HOSTS_LABEL &&
|
||||
selectedLabel?.count === 0
|
||||
) &&
|
||||
!(
|
||||
getStatusSelected() === ALL_HOSTS_LABEL &&
|
||||
filteredHostCount === 0
|
||||
) && (
|
||||
<Button
|
||||
variant="brand"
|
||||
onClick={toggleAddHostsModal}
|
||||
className={`${baseClass}__add-hosts`}
|
||||
>
|
||||
<span>Add hosts</span>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{renderActiveFilterBlock()}
|
||||
{renderNoEnrollSecretBanner()}
|
||||
{renderTable()}
|
||||
<div className={`${baseClass}`}>
|
||||
<div className="header-wrap">
|
||||
{renderHeader()}
|
||||
<div className={`${baseClass} button-wrap`}>
|
||||
{!isSandboxMode &&
|
||||
canEnrollHosts &&
|
||||
!hasHostErrors &&
|
||||
!hasHostCountErrors && (
|
||||
<Button
|
||||
onClick={() => setShowEnrollSecretModal(true)}
|
||||
className={`${baseClass}__enroll-hosts button`}
|
||||
variant="inverse"
|
||||
>
|
||||
<span>Manage enroll secret</span>
|
||||
</Button>
|
||||
)}
|
||||
{canEnrollHosts &&
|
||||
!hasHostErrors &&
|
||||
!hasHostCountErrors &&
|
||||
!(
|
||||
getStatusSelected() === ALL_HOSTS_LABEL &&
|
||||
selectedLabel?.count === 0
|
||||
) &&
|
||||
!(
|
||||
getStatusSelected() === ALL_HOSTS_LABEL &&
|
||||
filteredHostCount === 0
|
||||
) && (
|
||||
<Button
|
||||
onClick={toggleAddHostsModal}
|
||||
className={`${baseClass}__add-hosts button button--brand`}
|
||||
>
|
||||
<span>Add hosts</span>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
{renderActiveFilterBlock()}
|
||||
{renderNoEnrollSecretBanner()}
|
||||
{renderTable()}
|
||||
</div>
|
||||
</MainContent>
|
||||
{isAddLabel && (
|
||||
<SidePanelContent>
|
||||
<QuerySidePanel
|
||||
key="query-side-panel"
|
||||
onOsqueryTableSelect={onOsqueryTableSelect}
|
||||
selectedOsqueryTable={selectedOsqueryTable}
|
||||
/>
|
||||
</SidePanelContent>
|
||||
)}
|
||||
|
||||
{canEnrollHosts && showDeleteSecretModal && renderDeleteSecretModal()}
|
||||
{canEnrollHosts && showSecretEditorModal && renderSecretEditorModal()}
|
||||
{canEnrollHosts && showEnrollSecretModal && renderEnrollSecretModal()}
|
||||
|
@ -1,5 +1,3 @@
|
||||
export const NEW_LABEL_HASH = "#new_label";
|
||||
export const EDIT_LABEL_HASH = "#edit_label";
|
||||
export const ALL_HOSTS_LABEL = "all-hosts";
|
||||
export const LABEL_SLUG_PREFIX = "labels/";
|
||||
|
||||
|
@ -24,6 +24,7 @@ import EmailTokenRedirect from "components/EmailTokenRedirect";
|
||||
import ForgotPasswordPage from "pages/ForgotPasswordPage";
|
||||
import HostDetailsPage from "pages/hosts/details/HostDetailsPage";
|
||||
import Homepage from "pages/Homepage";
|
||||
import LabelPage from "pages/LabelPage";
|
||||
import LoginPage, { LoginPreviewPage } from "pages/LoginPage";
|
||||
import LogoutPage from "pages/LogoutPage";
|
||||
import ManageHostsPage from "pages/hosts/ManageHostsPage";
|
||||
@ -120,6 +121,11 @@ const routes = (
|
||||
<Route path="options" component={AgentOptionsPage} />
|
||||
</Route>
|
||||
</Route>
|
||||
<Route path="labels">
|
||||
<IndexRedirect to={"new"} />
|
||||
<Route path=":label_id" component={LabelPage} />
|
||||
<Route path="new" component={LabelPage} />
|
||||
</Route>
|
||||
<Route path="hosts">
|
||||
<IndexRedirect to={"manage"} />
|
||||
<Route path="manage" component={ManageHostsPage} />
|
||||
|
@ -25,6 +25,9 @@ export default {
|
||||
PACK: (packId: number): string => {
|
||||
return `${URL_PREFIX}/packs/${packId}`;
|
||||
},
|
||||
EDIT_LABEL: (labelId: number): string => {
|
||||
return `${URL_PREFIX}/labels/${labelId}`;
|
||||
},
|
||||
EDIT_QUERY: (query: IQuery): string => {
|
||||
return `${URL_PREFIX}/queries/${query.id}`;
|
||||
},
|
||||
@ -39,6 +42,9 @@ export default {
|
||||
LOGIN: `${URL_PREFIX}/login`,
|
||||
LOGOUT: `${URL_PREFIX}/logout`,
|
||||
MANAGE_HOSTS: `${URL_PREFIX}/hosts/manage`,
|
||||
MANAGE_HOSTS_LABEL: (labelId: number | string): string => {
|
||||
return `${URL_PREFIX}/hosts/manage/labels/${labelId}`;
|
||||
},
|
||||
HOST_DETAILS: (host: IHost): string => {
|
||||
return `${URL_PREFIX}/hosts/${host.id}`;
|
||||
},
|
||||
@ -63,6 +69,7 @@ export default {
|
||||
return `${URL_PREFIX}/schedule/manage/teams/${teamId}`;
|
||||
},
|
||||
MANAGE_POLICIES: `${URL_PREFIX}/policies/manage`,
|
||||
NEW_LABEL: `${URL_PREFIX}/labels/new`,
|
||||
NEW_POLICY: `${URL_PREFIX}/policies/new`,
|
||||
NEW_QUERY: `${URL_PREFIX}/queries/new`,
|
||||
RESET_PASSWORD: `${URL_PREFIX}/login/reset`,
|
||||
|
@ -566,7 +566,3 @@ export const DEFAULT_CREATE_USER_ERRORS = {
|
||||
password: "",
|
||||
sso_enabled: null,
|
||||
};
|
||||
|
||||
export const DEFAULT_CREATE_LABEL_ERRORS = {
|
||||
name: "",
|
||||
};
|
||||
|
@ -59,37 +59,6 @@ const labelSlug = (label: ILabel): string => {
|
||||
return `labels/${id}`;
|
||||
};
|
||||
|
||||
const labelStubs = [
|
||||
{
|
||||
id: "new",
|
||||
count: 0,
|
||||
description: "Hosts that have been enrolled to Fleet in the last 24 hours.",
|
||||
display_text: "New",
|
||||
slug: "new",
|
||||
statusLabelKey: "new_count",
|
||||
title_description: "(added in last 24hrs)",
|
||||
type: "status",
|
||||
},
|
||||
{
|
||||
id: "online",
|
||||
count: 0,
|
||||
description: "Hosts that have recently checked-in to Fleet.",
|
||||
display_text: "Online",
|
||||
slug: "online",
|
||||
statusLabelKey: "online_count",
|
||||
type: "status",
|
||||
},
|
||||
{
|
||||
id: "offline",
|
||||
count: 0,
|
||||
description: "Hosts that have not checked-in to Fleet recently.",
|
||||
display_text: "Offline",
|
||||
slug: "offline",
|
||||
statusLabelKey: "offline_count",
|
||||
type: "status",
|
||||
},
|
||||
];
|
||||
|
||||
const isLabel = (target: ISelectTargetsEntity) => {
|
||||
return "label_type" in target;
|
||||
};
|
||||
@ -250,7 +219,7 @@ const formatLabelResponse = (response: any): ILabel[] => {
|
||||
};
|
||||
});
|
||||
|
||||
return labels.concat(labelStubs);
|
||||
return labels;
|
||||
};
|
||||
|
||||
export const formatSelectedTargetsForApi = (
|
||||
|
Loading…
Reference in New Issue
Block a user