mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 00:45:19 +00:00
Fleet UI: macOS dashboard MDM solutions (#7014)
Co-authored-by: gillespi314 <73313222+gillespi314@users.noreply.github.com>
This commit is contained in:
parent
8d4ad6ce9f
commit
18852aae66
1
changes/issue-6928-mdm-solutions
Normal file
1
changes/issue-6928-mdm-solutions
Normal file
@ -0,0 +1 @@
|
||||
* MacOS dashboard view includes MDM solutions table and filters for hosts by MDM
|
@ -3,7 +3,7 @@ import React from "react";
|
||||
interface ITextCellProps {
|
||||
value: string | number | boolean;
|
||||
formatter?: (val: any) => string; // string, number, or null
|
||||
greyed?: string;
|
||||
greyed?: boolean;
|
||||
classes?: string;
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ const TextCell = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<span className={`text-cell ${classes} ${greyed || ""}`}>
|
||||
<span className={`text-cell ${classes} ${greyed && "grey-cell"}`}>
|
||||
{formatter(val)}
|
||||
</span>
|
||||
);
|
||||
|
@ -186,6 +186,10 @@
|
||||
min-width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.grey-cell {
|
||||
color: $ui-fleet-black-50;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
.highlight-on-hover:hover {
|
||||
background-color: $ui-off-white;
|
||||
|
@ -14,10 +14,18 @@ export interface IMDMAggregateStatus {
|
||||
unenrolled_hosts_count: number;
|
||||
}
|
||||
|
||||
export interface IMDMSolution {
|
||||
id: number;
|
||||
name: string | null;
|
||||
server_url: string;
|
||||
hosts_count: number;
|
||||
}
|
||||
|
||||
export interface IMacadminAggregate {
|
||||
macadmins: {
|
||||
counts_updated_at: string;
|
||||
munki_versions: IMunkiAggregate[];
|
||||
mobile_device_management_enrollment_status: IMDMAggregateStatus;
|
||||
mobile_device_management_solution: IMDMSolution[] | null;
|
||||
};
|
||||
}
|
||||
|
@ -274,7 +274,7 @@ const Homepage = (): JSX.Element => {
|
||||
});
|
||||
|
||||
const MDMCard = useInfoCard({
|
||||
title: "Mobile device management (MDM) enrollment",
|
||||
title: "Mobile device management (MDM)",
|
||||
showTitle: showMDMUI,
|
||||
description: (
|
||||
<p>
|
||||
|
@ -1,14 +1,24 @@
|
||||
import React, { useState } from "react";
|
||||
import { useQuery } from "react-query";
|
||||
import { Tab, Tabs, TabList, TabPanel } from "react-tabs";
|
||||
|
||||
import macadminsAPI from "services/entities/macadmins";
|
||||
import { IMacadminAggregate, IDataTableMDMFormat } from "interfaces/macadmins";
|
||||
import {
|
||||
IMacadminAggregate,
|
||||
IDataTableMDMFormat,
|
||||
IMDMSolution,
|
||||
} from "interfaces/macadmins";
|
||||
|
||||
import TabsWrapper from "components/TabsWrapper";
|
||||
import TableContainer from "components/TableContainer";
|
||||
import Spinner from "components/Spinner";
|
||||
import TableDataError from "components/DataError";
|
||||
import LastUpdatedText from "components/LastUpdatedText";
|
||||
import generateTableHeaders from "./MDMTableConfig";
|
||||
import {
|
||||
generateSolutionsTableHeaders,
|
||||
generateSolutionsDataSet,
|
||||
} from "./MDMSolutionsTableConfig";
|
||||
import generateEnrollmentTableHeaders from "./MDMEnrollmentTableConfig";
|
||||
|
||||
interface IMDMCardProps {
|
||||
showMDMUI: boolean;
|
||||
@ -18,13 +28,14 @@ interface IMDMCardProps {
|
||||
}
|
||||
|
||||
const DEFAULT_SORT_DIRECTION = "desc";
|
||||
const DEFAULT_SORT_HEADER = "hosts_count";
|
||||
const SOLUTIONS_DEFAULT_SORT_HEADER = "hosts_count";
|
||||
const ENROLLMENT_DEFAULT_SORT_HEADER = "hosts";
|
||||
const PAGE_SIZE = 8;
|
||||
const baseClass = "home-mdm";
|
||||
|
||||
const EmptyMDM = (): JSX.Element => (
|
||||
const EmptyMDMEnrollment = (): JSX.Element => (
|
||||
<div className={`${baseClass}__empty-mdm`}>
|
||||
<h1>Unable to detect MDM enrollment.</h1>
|
||||
<h1>Unable to detect MDM enrollment</h1>
|
||||
<p>
|
||||
To see MDM versions, deploy
|
||||
<a
|
||||
@ -39,15 +50,27 @@ const EmptyMDM = (): JSX.Element => (
|
||||
</div>
|
||||
);
|
||||
|
||||
const EmptyMDMSolutions = (): JSX.Element => (
|
||||
<div className={`${baseClass}__empty-mdm`}>
|
||||
<h1>No MDM solutions detected</h1>
|
||||
<p>
|
||||
This report is updated every hour to protect the performance of your
|
||||
devices.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
const MDM = ({
|
||||
showMDMUI,
|
||||
currentTeamId,
|
||||
setShowMDMUI,
|
||||
setTitleDetail,
|
||||
}: IMDMCardProps): JSX.Element => {
|
||||
const [navTabIndex, setNavTabIndex] = useState<number>(0);
|
||||
const [formattedMDMData, setFormattedMDMData] = useState<
|
||||
IDataTableMDMFormat[]
|
||||
>([]);
|
||||
const [solutions, setSolutions] = useState<IMDMSolution[] | null>([]);
|
||||
|
||||
const { isFetching: isMDMFetching, error: errorMDM } = useQuery<
|
||||
IMacadminAggregate,
|
||||
@ -58,6 +81,7 @@ const MDM = ({
|
||||
const {
|
||||
counts_updated_at,
|
||||
mobile_device_management_enrollment_status,
|
||||
mobile_device_management_solution,
|
||||
} = data.macadmins;
|
||||
const {
|
||||
enrolled_manual_hosts_count,
|
||||
@ -84,13 +108,20 @@ const MDM = ({
|
||||
},
|
||||
{ status: "Unenrolled", hosts: unenrolled_hosts_count },
|
||||
]);
|
||||
setSolutions(mobile_device_management_solution);
|
||||
},
|
||||
onError: () => {
|
||||
setShowMDMUI(true);
|
||||
},
|
||||
});
|
||||
|
||||
const tableHeaders = generateTableHeaders();
|
||||
const onTabChange = (index: number) => {
|
||||
setNavTabIndex(index);
|
||||
};
|
||||
|
||||
const solutionsTableHeaders = generateSolutionsTableHeaders();
|
||||
const enrollmentTableHeaders = generateEnrollmentTableHeaders();
|
||||
const solutionsDataSet = generateSolutionsDataSet(solutions);
|
||||
|
||||
// Renders opaque information as host information is loading
|
||||
const opacity = showMDMUI ? { opacity: 1 } : { opacity: 0 };
|
||||
@ -103,26 +134,58 @@ const MDM = ({
|
||||
</div>
|
||||
)}
|
||||
<div style={opacity}>
|
||||
{errorMDM ? (
|
||||
<TableDataError card />
|
||||
) : (
|
||||
<TableContainer
|
||||
columns={tableHeaders}
|
||||
data={formattedMDMData}
|
||||
isLoading={isMDMFetching}
|
||||
defaultSortHeader={DEFAULT_SORT_HEADER}
|
||||
defaultSortDirection={DEFAULT_SORT_DIRECTION}
|
||||
hideActionButton
|
||||
resultsTitle={"MDM"}
|
||||
emptyComponent={EmptyMDM}
|
||||
showMarkAllPages={false}
|
||||
isAllPagesSelected={false}
|
||||
disableCount
|
||||
disableActionButton
|
||||
disablePagination
|
||||
pageSize={PAGE_SIZE}
|
||||
/>
|
||||
)}
|
||||
<TabsWrapper>
|
||||
<Tabs selectedIndex={navTabIndex} onSelect={onTabChange}>
|
||||
<TabList>
|
||||
<Tab>Solutions</Tab>
|
||||
<Tab>Enrollment</Tab>
|
||||
</TabList>
|
||||
<TabPanel>
|
||||
{errorMDM ? (
|
||||
<TableDataError card />
|
||||
) : (
|
||||
<TableContainer
|
||||
columns={solutionsTableHeaders}
|
||||
data={solutionsDataSet}
|
||||
isLoading={isMDMFetching}
|
||||
defaultSortHeader={SOLUTIONS_DEFAULT_SORT_HEADER}
|
||||
defaultSortDirection={DEFAULT_SORT_DIRECTION}
|
||||
hideActionButton
|
||||
resultsTitle={"MDM"}
|
||||
emptyComponent={EmptyMDMSolutions}
|
||||
showMarkAllPages={false}
|
||||
isAllPagesSelected={false}
|
||||
isClientSidePagination
|
||||
disableCount
|
||||
disableActionButton
|
||||
pageSize={PAGE_SIZE}
|
||||
/>
|
||||
)}
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
{errorMDM ? (
|
||||
<TableDataError card />
|
||||
) : (
|
||||
<TableContainer
|
||||
columns={enrollmentTableHeaders}
|
||||
data={formattedMDMData}
|
||||
isLoading={isMDMFetching}
|
||||
defaultSortHeader={ENROLLMENT_DEFAULT_SORT_HEADER}
|
||||
defaultSortDirection={DEFAULT_SORT_DIRECTION}
|
||||
hideActionButton
|
||||
resultsTitle={"MDM"}
|
||||
emptyComponent={EmptyMDMEnrollment}
|
||||
showMarkAllPages={false}
|
||||
isAllPagesSelected={false}
|
||||
disableCount
|
||||
disableActionButton
|
||||
disablePagination
|
||||
pageSize={PAGE_SIZE}
|
||||
/>
|
||||
)}
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
</TabsWrapper>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
125
frontend/pages/Homepage/cards/MDM/MDMEnrollmentTableConfig.tsx
Normal file
125
frontend/pages/Homepage/cards/MDM/MDMEnrollmentTableConfig.tsx
Normal file
@ -0,0 +1,125 @@
|
||||
import React from "react";
|
||||
import { Link } from "react-router";
|
||||
|
||||
import { IDataTableMDMFormat } from "interfaces/macadmins";
|
||||
|
||||
import PATHS from "router/paths";
|
||||
import TextCell from "components/TableContainer/DataTable/TextCell";
|
||||
import TooltipWrapper from "components/TooltipWrapper";
|
||||
import Chevron from "../../../../../assets/images/icon-chevron-right-9x6@2x.png";
|
||||
|
||||
// NOTE: cellProps come from react-table
|
||||
// more info here https://react-table.tanstack.com/docs/api/useTable#cell-properties
|
||||
interface ICellProps {
|
||||
cell: {
|
||||
value: string;
|
||||
};
|
||||
row: {
|
||||
original: IDataTableMDMFormat;
|
||||
};
|
||||
}
|
||||
|
||||
interface IHeaderProps {
|
||||
column: {
|
||||
title: string;
|
||||
isSortedDesc: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
interface IStringCellProps extends ICellProps {
|
||||
cell: {
|
||||
value: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface IDataColumn {
|
||||
title: string;
|
||||
Header: ((props: IHeaderProps) => JSX.Element) | string;
|
||||
accessor: string;
|
||||
Cell: (props: ICellProps) => JSX.Element;
|
||||
disableHidden?: boolean;
|
||||
disableSortBy?: boolean;
|
||||
}
|
||||
|
||||
const enrollmentTableHeaders = [
|
||||
{
|
||||
title: "Status",
|
||||
Header: "Status",
|
||||
disableSortBy: true,
|
||||
accessor: "status",
|
||||
Cell: (cellProps: IStringCellProps) => {
|
||||
const tooltipText = (status: string): string => {
|
||||
if (status === "Enrolled (automatic)") {
|
||||
return `
|
||||
<span>
|
||||
Hosts automatically enrolled to an MDM solution <br/>
|
||||
the first time the host is used. Administrators <br />
|
||||
might have a higher level of control over these <br />
|
||||
hosts.
|
||||
</span>
|
||||
`;
|
||||
}
|
||||
return `
|
||||
<span>
|
||||
Hosts manually enrolled to an MDM solution by a<br />
|
||||
user or administrator.
|
||||
</span>
|
||||
`;
|
||||
};
|
||||
|
||||
if (cellProps.cell.value === "Unenrolled") {
|
||||
return <TextCell value={cellProps.cell.value} />;
|
||||
}
|
||||
return (
|
||||
<span className="name-container">
|
||||
<TooltipWrapper tipContent={tooltipText(cellProps.cell.value)}>
|
||||
{cellProps.cell.value}
|
||||
</TooltipWrapper>
|
||||
</span>
|
||||
);
|
||||
},
|
||||
sortType: "caseInsensitive",
|
||||
},
|
||||
{
|
||||
title: "Hosts",
|
||||
Header: "Hosts",
|
||||
disableSortBy: true,
|
||||
accessor: "hosts",
|
||||
Cell: (cellProps: ICellProps) => <TextCell value={cellProps.cell.value} />,
|
||||
},
|
||||
{
|
||||
title: "",
|
||||
Header: "",
|
||||
disableSortBy: true,
|
||||
disableGlobalFilter: true,
|
||||
accessor: "linkToFilteredHosts",
|
||||
Cell: (cellProps: IStringCellProps) => {
|
||||
const statusParam = () => {
|
||||
switch (cellProps.row.original.status) {
|
||||
case "Enrolled (automatic)":
|
||||
return "automatic";
|
||||
case "Enrolled (manual)":
|
||||
return "manual";
|
||||
default:
|
||||
return "unenrolled";
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Link
|
||||
to={`${PATHS.MANAGE_HOSTS}?mdm_enrollment_status=${statusParam()}`}
|
||||
className={`mdm-solution-link`}
|
||||
>
|
||||
View all hosts{" "}
|
||||
<img alt="link to hosts filtered by MDM solution" src={Chevron} />
|
||||
</Link>
|
||||
);
|
||||
},
|
||||
disableHidden: true,
|
||||
},
|
||||
];
|
||||
|
||||
const generateEnrollmentTableHeaders = (): IDataColumn[] => {
|
||||
return enrollmentTableHeaders;
|
||||
};
|
||||
|
||||
export default generateEnrollmentTableHeaders;
|
121
frontend/pages/Homepage/cards/MDM/MDMSolutionsTableConfig.tsx
Normal file
121
frontend/pages/Homepage/cards/MDM/MDMSolutionsTableConfig.tsx
Normal file
@ -0,0 +1,121 @@
|
||||
import React from "react";
|
||||
import { Link } from "react-router";
|
||||
|
||||
import { IMDMSolution } from "interfaces/macadmins";
|
||||
|
||||
import PATHS from "router/paths";
|
||||
import { greyCell } from "utilities/helpers";
|
||||
import HeaderCell from "components/TableContainer/DataTable/HeaderCell";
|
||||
import TextCell from "components/TableContainer/DataTable/TextCell";
|
||||
import Chevron from "../../../../../assets/images/icon-chevron-right-9x6@2x.png";
|
||||
|
||||
// NOTE: cellProps come from react-table
|
||||
// more info here https://react-table.tanstack.com/docs/api/useTable#cell-properties
|
||||
interface ICellProps {
|
||||
cell: {
|
||||
value: string;
|
||||
};
|
||||
row: {
|
||||
original: IMDMSolution;
|
||||
};
|
||||
}
|
||||
|
||||
interface IHeaderProps {
|
||||
column: {
|
||||
title: string;
|
||||
isSortedDesc: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
interface IStringCellProps extends ICellProps {
|
||||
cell: {
|
||||
value: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface IDataColumn {
|
||||
title: string;
|
||||
Header: ((props: IHeaderProps) => JSX.Element) | string;
|
||||
accessor: string;
|
||||
Cell: (props: ICellProps) => JSX.Element;
|
||||
disableHidden?: boolean;
|
||||
disableSortBy?: boolean;
|
||||
}
|
||||
|
||||
const solutionsTableHeaders = [
|
||||
{
|
||||
title: "Name",
|
||||
Header: "Name",
|
||||
disableSortBy: true,
|
||||
accessor: "name",
|
||||
Cell: (cellProps: ICellProps) => (
|
||||
<TextCell
|
||||
greyed={greyCell(cellProps.cell.value)}
|
||||
value={cellProps.cell.value}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Server URL",
|
||||
Header: "Server URL",
|
||||
disableSortBy: true,
|
||||
accessor: "server_url",
|
||||
Cell: (cellProps: ICellProps) => <TextCell value={cellProps.cell.value} />,
|
||||
},
|
||||
{
|
||||
title: "Hosts",
|
||||
Header: (cellProps: IHeaderProps) => (
|
||||
<HeaderCell
|
||||
value={cellProps.column.title}
|
||||
isSortedDesc={cellProps.column.isSortedDesc}
|
||||
/>
|
||||
),
|
||||
accessor: "hosts_count",
|
||||
Cell: (cellProps: ICellProps) => <TextCell value={cellProps.cell.value} />,
|
||||
},
|
||||
{
|
||||
title: "",
|
||||
Header: "",
|
||||
disableSortBy: true,
|
||||
disableGlobalFilter: true,
|
||||
accessor: "linkToFilteredHosts",
|
||||
Cell: (cellProps: IStringCellProps) => {
|
||||
return (
|
||||
<Link
|
||||
to={`${PATHS.MANAGE_HOSTS}?mdm_id=${cellProps.row.original.id}`}
|
||||
className={`mdm-solution-link`}
|
||||
>
|
||||
View all hosts{" "}
|
||||
<img alt="link to hosts filtered by MDM solution" src={Chevron} />
|
||||
</Link>
|
||||
);
|
||||
},
|
||||
disableHidden: true,
|
||||
},
|
||||
];
|
||||
|
||||
export const generateSolutionsTableHeaders = (): IDataColumn[] => {
|
||||
return solutionsTableHeaders;
|
||||
};
|
||||
|
||||
const enhanceSolutionsData = (solutions: IMDMSolution[]): IMDMSolution[] => {
|
||||
return Object.values(solutions).map((solution) => {
|
||||
return {
|
||||
id: solution.id,
|
||||
name: solution.name || "Unknown",
|
||||
server_url: solution.server_url,
|
||||
hosts_count: solution.hosts_count,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export const generateSolutionsDataSet = (
|
||||
solutions: IMDMSolution[] | null
|
||||
): IMDMSolution[] => {
|
||||
if (!solutions) {
|
||||
return [];
|
||||
}
|
||||
return [...enhanceSolutionsData(solutions)];
|
||||
};
|
||||
|
||||
export default { generateSolutionsTableHeaders, generateSolutionsDataSet };
|
@ -1,54 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
import { IDataTableMDMFormat } from "interfaces/macadmins";
|
||||
|
||||
import TextCell from "components/TableContainer/DataTable/TextCell";
|
||||
|
||||
// NOTE: cellProps come from react-table
|
||||
// more info here https://react-table.tanstack.com/docs/api/useTable#cell-properties
|
||||
interface ICellProps {
|
||||
cell: {
|
||||
value: string;
|
||||
};
|
||||
row: {
|
||||
original: IDataTableMDMFormat;
|
||||
};
|
||||
}
|
||||
|
||||
interface IHeaderProps {
|
||||
column: {
|
||||
title: string;
|
||||
isSortedDesc: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
interface IDataColumn {
|
||||
title: string;
|
||||
Header: ((props: IHeaderProps) => JSX.Element) | string;
|
||||
accessor: string;
|
||||
Cell: (props: ICellProps) => JSX.Element;
|
||||
disableHidden?: boolean;
|
||||
disableSortBy?: boolean;
|
||||
}
|
||||
|
||||
const munkiTableHeaders = [
|
||||
{
|
||||
title: "Status",
|
||||
Header: "Status",
|
||||
disableSortBy: true,
|
||||
accessor: "status",
|
||||
Cell: (cellProps: ICellProps) => <TextCell value={cellProps.cell.value} />,
|
||||
},
|
||||
{
|
||||
title: "Hosts",
|
||||
Header: "Hosts",
|
||||
accessor: "hosts",
|
||||
Cell: (cellProps: ICellProps) => <TextCell value={cellProps.cell.value} />,
|
||||
},
|
||||
];
|
||||
|
||||
const generateTableHeaders = (): IDataColumn[] => {
|
||||
return munkiTableHeaders;
|
||||
};
|
||||
|
||||
export default generateTableHeaders;
|
@ -2,14 +2,12 @@
|
||||
margin-top: $pad-large;
|
||||
position: relative;
|
||||
|
||||
.data-table__wrapper {
|
||||
overflow-x: auto;
|
||||
}
|
||||
.component__tabs-wrapper .table-container__header {
|
||||
display: none;
|
||||
}
|
||||
&__empty-mdm {
|
||||
margin: $pad-medium auto 0;
|
||||
width: 364px;
|
||||
margin: $pad-large auto 0;
|
||||
|
||||
h1 {
|
||||
font-size: $small;
|
||||
@ -36,39 +34,44 @@
|
||||
table-layout: fixed;
|
||||
|
||||
thead {
|
||||
.name__header {
|
||||
width: 60%;
|
||||
}
|
||||
.version__header {
|
||||
.name__header,
|
||||
.status__header {
|
||||
width: 30%;
|
||||
padding-right: 0;
|
||||
}
|
||||
.hosts_count__header {
|
||||
.server_url__header {
|
||||
width: 30%;
|
||||
}
|
||||
.hosts_count__header,
|
||||
.hosts__header {
|
||||
border-right: 0;
|
||||
padding-right: 0;
|
||||
width: 60px;
|
||||
}
|
||||
.id__header {
|
||||
padding: 0;
|
||||
border-left: 0;
|
||||
width: 40px;
|
||||
.linkToFilteredHosts__header {
|
||||
width: 140px;
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
.name__cell,
|
||||
.version__cell {
|
||||
.name__cell {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.id__cell {
|
||||
padding: 0;
|
||||
width: 40px;
|
||||
}
|
||||
.vulnerabilities__cell {
|
||||
.mdm-solution-link {
|
||||
visibility: hidden;
|
||||
text-overflow: none;
|
||||
img {
|
||||
transform: scale(0.5);
|
||||
vertical-align: text-top;
|
||||
width: 16px;
|
||||
}
|
||||
}
|
||||
tr {
|
||||
&:hover {
|
||||
.mdm-solution-link {
|
||||
visibility: visible;
|
||||
text-overflow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,11 +17,6 @@
|
||||
min-width: 542px;
|
||||
}
|
||||
|
||||
.grey-cell {
|
||||
color: $ui-fleet-black-50;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.modal__modal_container {
|
||||
width: 400px;
|
||||
a {
|
||||
|
@ -37,6 +37,7 @@ import {
|
||||
import { IApiError } from "interfaces/errors";
|
||||
import { IHost } from "interfaces/host";
|
||||
import { ILabel, ILabelFormData } from "interfaces/label";
|
||||
import { IMDMSolution } from "interfaces/macadmins";
|
||||
import { IOperatingSystemVersion } from "interfaces/operating_system";
|
||||
import { IPolicy } from "interfaces/policy";
|
||||
import { ISoftware } from "interfaces/software";
|
||||
@ -248,6 +249,10 @@ const ManageHostsPage = ({
|
||||
const [softwareDetails, setSoftwareDetails] = useState<ISoftware | null>(
|
||||
null
|
||||
);
|
||||
const [
|
||||
mdmSolutionDetails,
|
||||
setMDMSolutionDetails,
|
||||
] = useState<IMDMSolution | null>(null);
|
||||
const [tableQueryData, setTableQueryData] = useState<ITableQueryProps>();
|
||||
const [
|
||||
currentQueryOptions,
|
||||
@ -268,6 +273,11 @@ const ManageHostsPage = ({
|
||||
queryParams?.software_id !== undefined
|
||||
? parseInt(queryParams?.software_id, 10)
|
||||
: undefined;
|
||||
const mdmId =
|
||||
queryParams?.mdm_id !== undefined
|
||||
? parseInt(queryParams?.mdm_id, 10)
|
||||
: undefined;
|
||||
const mdmEnrollmentStatus = queryParams?.mdm_enrollment_status;
|
||||
const operatingSystemId =
|
||||
queryParams?.operating_system_id !== undefined
|
||||
? parseInt(queryParams?.operating_system_id, 10)
|
||||
@ -445,11 +455,15 @@ const ManageHostsPage = ({
|
||||
}
|
||||
|
||||
try {
|
||||
const { hosts: returnedHosts, software } = await hostsAPI.loadHosts(
|
||||
options
|
||||
);
|
||||
const {
|
||||
hosts: returnedHosts,
|
||||
software,
|
||||
mobile_device_management_solution,
|
||||
} = await hostsAPI.loadHosts(options);
|
||||
setHosts(returnedHosts);
|
||||
software && setSoftwareDetails(software);
|
||||
mobile_device_management_solution &&
|
||||
setMDMSolutionDetails(mobile_device_management_solution);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
setHasHostErrors(true);
|
||||
@ -534,6 +548,8 @@ const ManageHostsPage = ({
|
||||
policyId,
|
||||
policyResponse,
|
||||
softwareId,
|
||||
mdmId,
|
||||
mdmEnrollmentStatus,
|
||||
operatingSystemId,
|
||||
page: tableQueryData ? tableQueryData.pageIndex : 0,
|
||||
perPage: tableQueryData ? tableQueryData.pageSize : 100,
|
||||
@ -641,10 +657,18 @@ const ManageHostsPage = ({
|
||||
|
||||
const handleClearSoftwareFilter = () => {
|
||||
router.replace(PATHS.MANAGE_HOSTS);
|
||||
setCurrentTeam(undefined);
|
||||
setSoftwareDetails(null);
|
||||
};
|
||||
|
||||
const handleClearMDMSolutionFilter = () => {
|
||||
router.replace(PATHS.MANAGE_HOSTS);
|
||||
setMDMSolutionDetails(null);
|
||||
};
|
||||
|
||||
const handleClearMDMEnrollmentFilter = () => {
|
||||
router.replace(PATHS.MANAGE_HOSTS);
|
||||
};
|
||||
|
||||
const handleTeamSelect = (teamId: number) => {
|
||||
const { MANAGE_HOSTS } = PATHS;
|
||||
const teamIdParam = getValidatedTeamId(
|
||||
@ -769,10 +793,25 @@ const ManageHostsPage = ({
|
||||
newQueryParams.policy_response = policyResponse;
|
||||
}
|
||||
|
||||
if (softwareId && !policyId) {
|
||||
if (softwareId && !policyId && !mdmId && !mdmEnrollmentStatus) {
|
||||
newQueryParams.software_id = softwareId;
|
||||
}
|
||||
if (operatingSystemId && !softwareId && !policyId) {
|
||||
|
||||
if (mdmId && !policyId && !softwareId && !mdmEnrollmentStatus) {
|
||||
newQueryParams.mdm_id = mdmId;
|
||||
}
|
||||
|
||||
if (mdmEnrollmentStatus && !policyId && !softwareId && !mdmId) {
|
||||
newQueryParams.mdm_enrollment_status = mdmEnrollmentStatus;
|
||||
}
|
||||
|
||||
if (
|
||||
operatingSystemId &&
|
||||
!softwareId &&
|
||||
!policyId &&
|
||||
!mdmEnrollmentStatus &&
|
||||
!mdmId
|
||||
) {
|
||||
newQueryParams.operating_system_id = operatingSystemId;
|
||||
}
|
||||
router.replace(
|
||||
@ -791,6 +830,8 @@ const ManageHostsPage = ({
|
||||
policyId,
|
||||
queryParams,
|
||||
softwareId,
|
||||
mdmId,
|
||||
mdmEnrollmentStatus,
|
||||
operatingSystemId,
|
||||
sortBy,
|
||||
]
|
||||
@ -1029,6 +1070,8 @@ const ManageHostsPage = ({
|
||||
policyId,
|
||||
policyResponse,
|
||||
softwareId,
|
||||
mdmId,
|
||||
mdmEnrollmentStatus,
|
||||
});
|
||||
|
||||
toggleTransferHostModal();
|
||||
@ -1074,6 +1117,8 @@ const ManageHostsPage = ({
|
||||
policyId,
|
||||
policyResponse,
|
||||
softwareId,
|
||||
mdmId,
|
||||
mdmEnrollmentStatus,
|
||||
});
|
||||
|
||||
refetchLabels();
|
||||
@ -1188,12 +1233,12 @@ const ManageHostsPage = ({
|
||||
>
|
||||
{buttonText}
|
||||
<Button
|
||||
className={`${baseClass}__clear-policies-filter`}
|
||||
className={`${baseClass}__clear-software-filter`}
|
||||
onClick={handleClearSoftwareFilter}
|
||||
variant={"small-text-icon"}
|
||||
title={buttonText}
|
||||
>
|
||||
<img src={CloseIcon} alt="Remove policy filter" />
|
||||
<img src={CloseIcon} alt="Remove software filter" />
|
||||
</Button>
|
||||
</div>
|
||||
</span>
|
||||
@ -1216,6 +1261,126 @@ const ManageHostsPage = ({
|
||||
return null;
|
||||
};
|
||||
|
||||
const renderMDMSolutionFilterBlock = () => {
|
||||
if (mdmSolutionDetails) {
|
||||
const { name, server_url } = mdmSolutionDetails;
|
||||
const buttonText = `${name !== "Unknown" && name} ${server_url}`;
|
||||
return (
|
||||
<div className={`${baseClass}__mdm-solution-filter-block`}>
|
||||
<div>
|
||||
<span
|
||||
data-tip
|
||||
data-for="mdm-solution-filter-tooltip"
|
||||
data-tip-disable={!name || !server_url}
|
||||
>
|
||||
<div
|
||||
className={`${baseClass}__mdm-solution-filter-name-card tooltip`}
|
||||
>
|
||||
{buttonText}
|
||||
<Button
|
||||
className={`${baseClass}__clear-mdm-solution-filter`}
|
||||
onClick={handleClearMDMSolutionFilter}
|
||||
variant={"small-text-icon"}
|
||||
title={buttonText}
|
||||
>
|
||||
<img src={CloseIcon} alt="Remove MDM solution filter" />
|
||||
</Button>
|
||||
</div>
|
||||
</span>
|
||||
<ReactTooltip
|
||||
place="bottom"
|
||||
effect="solid"
|
||||
backgroundColor="#3e4771"
|
||||
id="mdm-solution-filter-tooltip"
|
||||
data-html
|
||||
>
|
||||
<span className={`tooltip__tooltip-text`}>
|
||||
Host enrolled
|
||||
{name !== "Unknown" && ` to ${name}`}
|
||||
<br /> at {server_url}
|
||||
</span>
|
||||
</ReactTooltip>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const renderMDMEnrollmentFilterBlock = () => {
|
||||
if (mdmEnrollmentStatus) {
|
||||
const buttonText = () => {
|
||||
switch (mdmEnrollmentStatus) {
|
||||
case "automatic":
|
||||
return "MDM enrolled (automatic)";
|
||||
case "manual":
|
||||
return "MDM enrolled (manual)";
|
||||
default:
|
||||
return "Unenrolled";
|
||||
}
|
||||
};
|
||||
const tooltipText = () => {
|
||||
switch (mdmEnrollmentStatus) {
|
||||
case "automatic":
|
||||
return (
|
||||
<span className={`tooltip__tooltip-text`}>
|
||||
Hosts automatically enrolled <br />
|
||||
to an MDM solution the first time <br />
|
||||
the host is used. Administrators <br />
|
||||
might have a higher level of control <br />
|
||||
over these hosts.
|
||||
</span>
|
||||
);
|
||||
case "manual":
|
||||
return (
|
||||
<span className={`tooltip__tooltip-text`}>
|
||||
Hosts manually enrolled to an <br />
|
||||
MDM solution by a user or <br />
|
||||
administrator.
|
||||
</span>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<span className={`tooltip__tooltip-text`}>
|
||||
Hosts not enrolled to <br /> an MDM solution.
|
||||
</span>
|
||||
);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className={`${baseClass}__mdm-enrollment-status-filter-block`}>
|
||||
<div>
|
||||
<span data-tip data-for="mdm-enrollment-status-filter-tooltip">
|
||||
<div
|
||||
className={`${baseClass}__mdm-enrollment-status-filter-name-card tooltip`}
|
||||
>
|
||||
{buttonText()}
|
||||
<Button
|
||||
className={`${baseClass}__clear-mdm-enrollment-status-filter`}
|
||||
onClick={handleClearMDMEnrollmentFilter}
|
||||
variant={"small-text-icon"}
|
||||
title={buttonText()}
|
||||
>
|
||||
<img src={CloseIcon} alt="Remove MDM enrollment filter" />
|
||||
</Button>
|
||||
</div>
|
||||
</span>
|
||||
<ReactTooltip
|
||||
place="bottom"
|
||||
effect="solid"
|
||||
backgroundColor="#3e4771"
|
||||
id="mdm-enrollment-status-filter-tooltip"
|
||||
data-html
|
||||
>
|
||||
{tooltipText()}
|
||||
</ReactTooltip>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const renderEditColumnsModal = () => {
|
||||
if (!config || !currentUser) {
|
||||
return null;
|
||||
@ -1404,6 +1569,8 @@ const ManageHostsPage = ({
|
||||
policyId,
|
||||
policyResponse,
|
||||
softwareId,
|
||||
mdmId,
|
||||
mdmEnrollmentStatus,
|
||||
visibleColumns,
|
||||
};
|
||||
|
||||
@ -1471,22 +1638,47 @@ const ManageHostsPage = ({
|
||||
selectedLabel &&
|
||||
selectedLabel.type !== "all" &&
|
||||
selectedLabel.type !== "status";
|
||||
if (policyId || softwareId || showSelectedLabel || operatingSystemId) {
|
||||
if (
|
||||
policyId ||
|
||||
softwareId ||
|
||||
showSelectedLabel ||
|
||||
mdmId ||
|
||||
mdmEnrollmentStatus ||
|
||||
operatingSystemId
|
||||
) {
|
||||
return (
|
||||
<div className={`${baseClass}__labels-active-filter-wrap`}>
|
||||
{showSelectedLabel && renderHeaderLabelBlock()}
|
||||
{!!policyId &&
|
||||
!softwareId &&
|
||||
!mdmId &&
|
||||
!mdmEnrollmentStatus &&
|
||||
!showSelectedLabel &&
|
||||
renderPoliciesFilterBlock()}
|
||||
{!!softwareId &&
|
||||
!policyId &&
|
||||
!mdmId &&
|
||||
!mdmEnrollmentStatus &&
|
||||
!showSelectedLabel &&
|
||||
renderSoftwareFilterBlock()}
|
||||
{!!mdmId &&
|
||||
!policyId &&
|
||||
!softwareId &&
|
||||
!mdmEnrollmentStatus &&
|
||||
!showSelectedLabel &&
|
||||
renderMDMSolutionFilterBlock()}
|
||||
{!!mdmEnrollmentStatus &&
|
||||
!policyId &&
|
||||
!softwareId &&
|
||||
!mdmId &&
|
||||
!showSelectedLabel &&
|
||||
renderMDMEnrollmentFilterBlock()}
|
||||
{!!operatingSystemId &&
|
||||
!policyId &&
|
||||
!softwareId &&
|
||||
!showSelectedLabel &&
|
||||
!mdmId &&
|
||||
!mdmEnrollmentStatus &&
|
||||
renderOSFilterBlock()}
|
||||
</div>
|
||||
);
|
||||
@ -1587,10 +1779,18 @@ const ManageHostsPage = ({
|
||||
!isHostsLoading &&
|
||||
teamSync
|
||||
) {
|
||||
const { software_id, policy_id, operating_system_id } = queryParams || {};
|
||||
const includesSoftwareOrPolicyOrOSFilter = !!(
|
||||
const {
|
||||
software_id,
|
||||
policy_id,
|
||||
mdm_id,
|
||||
mdm_enrollment_status,
|
||||
operating_system_id,
|
||||
} = queryParams || {};
|
||||
const includesNameCardFilter = !!(
|
||||
software_id ||
|
||||
policy_id ||
|
||||
mdm_id ||
|
||||
mdm_enrollment_status ||
|
||||
operating_system_id
|
||||
);
|
||||
|
||||
@ -1598,7 +1798,7 @@ const ManageHostsPage = ({
|
||||
<NoHosts
|
||||
toggleAddHostsModal={toggleAddHostsModal}
|
||||
canEnrollHosts={canEnrollHosts}
|
||||
includesSoftwareOrPolicyFilter={includesSoftwareOrPolicyOrOSFilter}
|
||||
includesNameCardFilter={includesNameCardFilter}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -199,6 +199,7 @@
|
||||
&__policies-filter-block {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $pad-medium;
|
||||
|
||||
p {
|
||||
font-size: $xx-small;
|
||||
@ -208,14 +209,15 @@
|
||||
}
|
||||
|
||||
&__policies-filter-name-card,
|
||||
&__software-filter-name-card {
|
||||
&__software-filter-name-card,
|
||||
&__mdm-solution-filter-name-card,
|
||||
&__mdm-enrollment-status-filter-name-card {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 6px 12px;
|
||||
border: 1px solid $ui-fleet-black-25;
|
||||
border-radius: 4px;
|
||||
box-shadow: none;
|
||||
margin-left: $pad-medium;
|
||||
color: $core-fleet-black;
|
||||
font-size: $xx-small;
|
||||
font-weight: $bold;
|
||||
@ -240,10 +242,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__software-filter-name-card {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
&__enroll-hosts {
|
||||
padding: $pad-small;
|
||||
margin-right: $pad-small;
|
||||
|
@ -9,7 +9,7 @@ import RoboDogImage from "../../../../../../assets/images/robo-dog-176x144@2x.pn
|
||||
interface INoHostsProps {
|
||||
toggleAddHostsModal: () => void;
|
||||
canEnrollHosts?: boolean;
|
||||
includesSoftwareOrPolicyFilter?: boolean;
|
||||
includesNameCardFilter?: boolean;
|
||||
}
|
||||
|
||||
const baseClass = "no-hosts";
|
||||
@ -17,10 +17,10 @@ const baseClass = "no-hosts";
|
||||
const NoHosts = ({
|
||||
toggleAddHostsModal,
|
||||
canEnrollHosts,
|
||||
includesSoftwareOrPolicyFilter,
|
||||
includesNameCardFilter,
|
||||
}: INoHostsProps): JSX.Element => {
|
||||
const renderContent = () => {
|
||||
if (includesSoftwareOrPolicyFilter) {
|
||||
if (includesNameCardFilter) {
|
||||
return (
|
||||
<div>
|
||||
<h1>No hosts match the current criteria</h1>
|
||||
@ -64,9 +64,7 @@ const NoHosts = ({
|
||||
return (
|
||||
<div className={`${baseClass}`}>
|
||||
<div className={`${baseClass}__inner`}>
|
||||
{!includesSoftwareOrPolicyFilter && (
|
||||
<img src={RoboDogImage} alt="No Hosts" />
|
||||
)}
|
||||
{!includesNameCardFilter && <img src={RoboDogImage} alt="No Hosts" />}
|
||||
{renderContent()}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -17,6 +17,8 @@ export interface IHostCountLoadOptions {
|
||||
policyId?: number;
|
||||
policyResponse?: string;
|
||||
softwareId?: number;
|
||||
mdmId?: number;
|
||||
mdmEnrollmentStatus?: string;
|
||||
operatingSystemId?: number;
|
||||
}
|
||||
|
||||
@ -30,6 +32,8 @@ export default {
|
||||
const policyResponse = options?.policyResponse || null;
|
||||
const selectedLabels = options?.selectedLabels || [];
|
||||
const softwareId = options?.softwareId || null;
|
||||
const mdmId = options?.mdmId || null;
|
||||
const mdmEnrollmentStatus = options?.mdmEnrollmentStatus || null;
|
||||
const operatingSystemId = options?.operatingSystemId || null;
|
||||
|
||||
const labelPrefix = "labels/";
|
||||
@ -70,7 +74,22 @@ export default {
|
||||
queryString += `&software_id=${softwareId}`;
|
||||
}
|
||||
|
||||
if (!label && !policyId && !softwareId && operatingSystemId) {
|
||||
if (!label && !policyId && mdmId) {
|
||||
queryString += `&mdm_id=${mdmId}`;
|
||||
}
|
||||
|
||||
if (!label && !policyId && mdmEnrollmentStatus) {
|
||||
queryString += `&mdm_enrollment_status=${mdmEnrollmentStatus}`;
|
||||
}
|
||||
|
||||
if (
|
||||
!label &&
|
||||
!policyId &&
|
||||
!softwareId &&
|
||||
!mdmId &&
|
||||
!mdmEnrollmentStatus &&
|
||||
operatingSystemId
|
||||
) {
|
||||
queryString += `&operating_system_id=${operatingSystemId}`;
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,8 @@ export interface ILoadHostsOptions {
|
||||
policyId?: number;
|
||||
policyResponse?: string;
|
||||
softwareId?: number;
|
||||
mdmId?: number;
|
||||
mdmEnrollmentStatus?: string;
|
||||
operatingSystemId?: number;
|
||||
device_mapping?: boolean;
|
||||
columns?: string;
|
||||
@ -35,6 +37,8 @@ export interface IExportHostsOptions {
|
||||
policyId?: number;
|
||||
policyResponse?: string;
|
||||
softwareId?: number;
|
||||
mdmId?: number;
|
||||
mdmEnrollmentStatus?: string;
|
||||
operatingSystemId?: number;
|
||||
device_mapping?: boolean;
|
||||
columns?: string;
|
||||
@ -101,9 +105,37 @@ const getPolicyParams = (
|
||||
const getSoftwareParam = (
|
||||
label?: string,
|
||||
policyId?: number,
|
||||
softwareId?: number
|
||||
softwareId?: number,
|
||||
mdmId?: number,
|
||||
mdmEnrollmentStatus?: string
|
||||
) => {
|
||||
return label === undefined && policyId === undefined ? softwareId : undefined;
|
||||
return !label && !policyId && !mdmId && !mdmEnrollmentStatus
|
||||
? softwareId
|
||||
: undefined;
|
||||
};
|
||||
|
||||
const getMDMSolutionParam = (
|
||||
label?: string,
|
||||
policyId?: number,
|
||||
softwareId?: number,
|
||||
mdmId?: number,
|
||||
mdmEnrollmentStatus?: string
|
||||
) => {
|
||||
return !label && !policyId && !softwareId && !mdmEnrollmentStatus
|
||||
? mdmId
|
||||
: undefined;
|
||||
};
|
||||
|
||||
const getMDMEnrollmentStatusParam = (
|
||||
label?: string,
|
||||
policyId?: number,
|
||||
softwareId?: number,
|
||||
mdmId?: number,
|
||||
mdmEnrollmentStatus?: string
|
||||
) => {
|
||||
return !label && !policyId && !softwareId && !mdmId
|
||||
? mdmEnrollmentStatus
|
||||
: undefined;
|
||||
};
|
||||
|
||||
const getOperatingSystemParam = (
|
||||
@ -156,6 +188,8 @@ export default {
|
||||
const policyId = options?.policyId || null;
|
||||
const policyResponse = options?.policyResponse || "passing";
|
||||
const softwareId = options?.softwareId || null;
|
||||
const mdmId = options?.mdmId || null;
|
||||
const mdmEnrollmentStatus = options?.mdmEnrollmentStatus || null;
|
||||
const visibleColumns = options?.visibleColumns || null;
|
||||
|
||||
if (!sortBy.length) {
|
||||
@ -186,7 +220,7 @@ export default {
|
||||
path += `&team_id=${teamId}`;
|
||||
}
|
||||
|
||||
// Label OR policy_id OR software_id are valid filters.
|
||||
// label OR policy_id OR software_id OR mdm_id OR mdm_enrollment_status are valid filters.
|
||||
if (label) {
|
||||
const lid = label.substr(labelPrefix.length);
|
||||
path += `&label_id=${parseInt(lid, 10)}`;
|
||||
@ -197,10 +231,18 @@ export default {
|
||||
path += `&policy_response=${policyResponse}`;
|
||||
}
|
||||
|
||||
if (!label && !policyId && softwareId) {
|
||||
if (!label && !policyId && !mdmId && !mdmEnrollmentStatus && softwareId) {
|
||||
path += `&software_id=${softwareId}`;
|
||||
}
|
||||
|
||||
if (!label && !policyId && !softwareId && !mdmEnrollmentStatus && mdmId) {
|
||||
path += `&mdm_id=${mdmId}`;
|
||||
}
|
||||
|
||||
if (!label && !policyId && !softwareId && !mdmId && mdmEnrollmentStatus) {
|
||||
path += `&mdm_enrollment_status=${mdmEnrollmentStatus}`;
|
||||
}
|
||||
|
||||
if (visibleColumns) {
|
||||
path += `&columns=${visibleColumns}`;
|
||||
}
|
||||
@ -217,6 +259,8 @@ export default {
|
||||
policyId,
|
||||
policyResponse = "passing",
|
||||
softwareId,
|
||||
mdmId,
|
||||
mdmEnrollmentStatus,
|
||||
operatingSystemId,
|
||||
device_mapping,
|
||||
selectedLabels,
|
||||
@ -237,6 +281,20 @@ export default {
|
||||
policy_id: policyParams.policy_id,
|
||||
policy_response: policyParams.policy_response,
|
||||
software_id: getSoftwareParam(label, policyId, softwareId),
|
||||
mdm_id: getMDMSolutionParam(
|
||||
label,
|
||||
policyId,
|
||||
softwareId,
|
||||
mdmId,
|
||||
mdmEnrollmentStatus
|
||||
),
|
||||
mdm_enrollment_status: getMDMEnrollmentStatusParam(
|
||||
label,
|
||||
policyId,
|
||||
softwareId,
|
||||
mdmId,
|
||||
mdmEnrollmentStatus
|
||||
),
|
||||
operating_system_id: getOperatingSystemParam(
|
||||
label,
|
||||
policyId,
|
||||
@ -248,6 +306,7 @@ export default {
|
||||
};
|
||||
|
||||
const queryString = buildQueryStringFromParams(queryParams);
|
||||
|
||||
const endpoint = getHostEndpoint(selectedLabels);
|
||||
const path = `${endpoint}?${queryString}`;
|
||||
return sendRequest("GET", path);
|
||||
|
@ -8,6 +8,7 @@ export default {
|
||||
const { MACADMINS } = endpoints;
|
||||
const queryString = buildQueryStringFromParams({ team_id: teamId });
|
||||
const path = `${MACADMINS}?${queryString}`;
|
||||
|
||||
return sendRequest("GET", path);
|
||||
},
|
||||
};
|
||||
|
@ -358,4 +358,8 @@ const labels = {
|
||||
],
|
||||
};
|
||||
|
||||
export default { count, hosts, labels };
|
||||
export default {
|
||||
count,
|
||||
hosts,
|
||||
labels,
|
||||
};
|
||||
|
@ -547,16 +547,12 @@ export const generateTeam = (
|
||||
return `${teams.length + 1} teams`; // global role and one or more teams
|
||||
};
|
||||
|
||||
export const greyCell = (roleOrTeamText: string): string => {
|
||||
const GREYED_TEXT = ["Global", "Unassigned", "Various", "No Team"];
|
||||
export const greyCell = (roleOrTeamText: string): boolean => {
|
||||
const GREYED_TEXT = ["Global", "Unassigned", "Various", "No Team", "Unknown"];
|
||||
|
||||
if (
|
||||
GREYED_TEXT.includes(roleOrTeamText) ||
|
||||
roleOrTeamText.includes(" teams")
|
||||
) {
|
||||
return "grey-cell";
|
||||
}
|
||||
return "";
|
||||
return (
|
||||
GREYED_TEXT.includes(roleOrTeamText) || roleOrTeamText.includes(" teams")
|
||||
);
|
||||
};
|
||||
|
||||
const setupData = (formData: any) => {
|
||||
|
Loading…
Reference in New Issue
Block a user