diff --git a/frontend/components/AddHostsModal/AddHostsModal.tests.tsx b/frontend/components/AddHostsModal/AddHostsModal.tests.tsx
index f3368a5140af..658bbd11882e 100644
--- a/frontend/components/AddHostsModal/AddHostsModal.tests.tsx
+++ b/frontend/components/AddHostsModal/AddHostsModal.tests.tsx
@@ -57,15 +57,13 @@ describe("AddHostsModal", () => {
expect(windowsText).toBeInTheDocument();
expect(screen.queryByText(/--enable-scripts/i)).toBeInTheDocument();
- await user.click(screen.getByRole("tab", { name: "Linux (RPM)" }));
- const linuxRPMText = screen.getByText(/--type=rpm/i);
- expect(linuxRPMText).toBeInTheDocument();
- expect(screen.queryByText(/--enable-scripts/i)).toBeInTheDocument();
-
- await user.click(screen.getByRole("tab", { name: "Linux (deb)" }));
+ await user.click(screen.getByRole("tab", { name: "Linux" }));
const linuxDebText = screen.getByText(/--type=deb/i);
expect(linuxDebText).toBeInTheDocument();
expect(screen.queryByText(/--enable-scripts/i)).toBeInTheDocument();
+ expect(
+ screen.queryByText(/CentOS, Red Hat, and Fedora Linux, use --type=rpm/i)
+ ).toBeInTheDocument();
await user.click(screen.getByRole("tab", { name: "ChromeOS" }));
const extensionId = screen.getByDisplayValue(
@@ -74,6 +72,10 @@ describe("AddHostsModal", () => {
expect(extensionId).toBeInTheDocument();
expect(screen.queryByText(/--enable-scripts/i)).not.toBeInTheDocument();
+ await user.click(screen.getByRole("tab", { name: "iOS & iPadOS" }));
+ expect(screen.queryByText(/Apple Business Manager/i)).toBeInTheDocument();
+ expect(screen.queryByText(/Learn more/i)).toBeInTheDocument();
+
await user.click(screen.getByRole("tab", { name: "Advanced" }));
const advancedText = screen.getByText(/--type=YOUR_TYPE/i);
expect(advancedText).toBeInTheDocument();
@@ -146,41 +148,6 @@ describe("AddHostsModal", () => {
expect(ctaButton).toBeEnabled();
});
- it("sandbox mode renders and download disabled until a platform is selected", async () => {
- const render = createCustomRenderer({
- withBackendMock: true,
- context: {
- app: {
- isPreviewMode: false,
- config: createMockConfig(),
- },
- },
- });
-
- const { user } = render(
-
- );
-
- const text = screen.getByText("Which platform is your host running?");
- const windowsText = screen.getByText("Windows");
- const downloadButton = screen.getByRole("button", {
- name: /Download installer/i,
- });
-
- expect(text).toBeInTheDocument();
- expect(downloadButton).not.toBeEnabled();
-
- await user.click(windowsText);
-
- expect(downloadButton).toBeEnabled();
- });
-
it("excludes `--enable-scripts` flag if `config.server_settings.scripts-disabled` is `true`", async () => {
const mockConfig = createMockConfig();
mockConfig.server_settings.scripts_disabled = true;
@@ -214,16 +181,11 @@ describe("AddHostsModal", () => {
expect(windowsText).toBeInTheDocument();
expect(screen.queryByText(/--enable-scripts/i)).not.toBeInTheDocument();
- await user.click(screen.getByRole("tab", { name: "Linux (RPM)" }));
+ await user.click(screen.getByRole("tab", { name: "Linux" }));
const linuxRPMText = screen.getByText(/--type=rpm/i);
expect(linuxRPMText).toBeInTheDocument();
expect(screen.queryByText(/--enable-scripts/i)).not.toBeInTheDocument();
- await user.click(screen.getByRole("tab", { name: "Linux (deb)" }));
- const linuxDebText = screen.getByText(/--type=deb/i);
- expect(linuxDebText).toBeInTheDocument();
- expect(screen.queryByText(/--enable-scripts/i)).not.toBeInTheDocument();
-
await user.click(screen.getByRole("tab", { name: "ChromeOS" }));
const extensionId = screen.getByDisplayValue(
/fleeedmmihkfkeemmipgmhhjemlljidg/i
diff --git a/frontend/components/AddHostsModal/AddHostsModal.tsx b/frontend/components/AddHostsModal/AddHostsModal.tsx
index 7d74f7cfc3c2..04a42908ed2e 100644
--- a/frontend/components/AddHostsModal/AddHostsModal.tsx
+++ b/frontend/components/AddHostsModal/AddHostsModal.tsx
@@ -18,7 +18,6 @@ interface IAddHostsModal {
enrollSecret?: string;
isAnyTeamSelected: boolean;
isLoading: boolean;
- isSandboxMode?: boolean;
onCancel: () => void;
openEnrollSecretModal?: () => void;
}
@@ -28,7 +27,6 @@ const AddHostsModal = ({
enrollSecret,
isAnyTeamSelected,
isLoading,
- isSandboxMode,
onCancel,
openEnrollSecretModal,
}: IAddHostsModal): JSX.Element => {
@@ -53,12 +51,6 @@ const AddHostsModal = ({
openEnrollSecretModal && openEnrollSecretModal();
};
- // TODO: Currently, prepacked installers in Fleet Sandbox use the global enroll secret,
- // and Fleet Sandbox runs Fleet Free so the currentTeam check here is an
- // additional precaution/reminder to revisit this in connection with future changes.
- // See https://github.com/fleetdm/fleet/issues/4970#issuecomment-1187679407.
- const shouldRenderDownloadInstallersContent =
- isSandboxMode && !isAnyTeamSelected;
const renderModalContent = () => {
if (isLoading) {
return ;
@@ -81,9 +73,7 @@ const AddHostsModal = ({
);
}
- return shouldRenderDownloadInstallersContent ? (
-
- ) : (
+ return (
);
}
+
+ if (packageType === "ios-ipados") {
+ return (
+
+
+ Enroll iPhones and iPads by adding them to Fleet in Apple Business
+ Manager (ABM).{" "}
+
+
+
+ );
+ }
+
if (packageType === "advanced") {
return (
<>
@@ -539,7 +556,11 @@ const PlatformWrapper = ({
label={renderLabel(packageType, renderInstallerString(packageType))}
type="textarea"
value={renderInstallerString(packageType)}
- helpText="Distribute your package to add hosts to Fleet."
+ helpText={`Distribute your package to add hosts to Fleet.${
+ packageType === "deb"
+ ? " For CentOS, Red Hat, and Fedora Linux, use --type=rpm."
+ : ""
+ }`}
/>
>
);
diff --git a/frontend/components/AddHostsModal/PlatformWrapper/_styles.scss b/frontend/components/AddHostsModal/PlatformWrapper/_styles.scss
index b032d830b287..a038b91704e8 100644
--- a/frontend/components/AddHostsModal/PlatformWrapper/_styles.scss
+++ b/frontend/components/AddHostsModal/PlatformWrapper/_styles.scss
@@ -8,7 +8,7 @@
position: initial;
.react-tabs {
&__tab-list {
- margin: 0 0 1.25rem;
+ margin: 0 0 $pad-large;
}
}
}
diff --git a/frontend/components/PlatformCompatibility/PlatformCompatibility.tests.tsx b/frontend/components/PlatformCompatibility/PlatformCompatibility.tests.tsx
index b0fc1c950a56..10fbbb4faf7e 100644
--- a/frontend/components/PlatformCompatibility/PlatformCompatibility.tests.tsx
+++ b/frontend/components/PlatformCompatibility/PlatformCompatibility.tests.tsx
@@ -7,7 +7,7 @@ describe("Platform compatibility", () => {
it("renders compatible platforms", () => {
render(
);
@@ -38,7 +38,7 @@ describe("Platform compatibility", () => {
it("renders error state", () => {
render(
);
diff --git a/frontend/components/PlatformCompatibility/PlatformCompatibility.tsx b/frontend/components/PlatformCompatibility/PlatformCompatibility.tsx
index 973647315a1e..700726d37ecb 100644
--- a/frontend/components/PlatformCompatibility/PlatformCompatibility.tsx
+++ b/frontend/components/PlatformCompatibility/PlatformCompatibility.tsx
@@ -1,13 +1,17 @@
import React from "react";
-import { OsqueryPlatform } from "interfaces/platform";
+import {
+ DisplayPlatform,
+ QueryableDisplayPlatform,
+ QueryablePlatform,
+} from "interfaces/platform";
import { PLATFORM_DISPLAY_NAMES } from "utilities/constants";
import TooltipWrapper from "components/TooltipWrapper";
import Icon from "components/Icon";
interface IPlatformCompatibilityProps {
- compatiblePlatforms: OsqueryPlatform[] | null;
+ compatiblePlatforms: any[] | null;
error: Error | null;
}
@@ -18,13 +22,13 @@ const DISPLAY_ORDER = [
"Windows",
"Linux",
"ChromeOS",
-] as OsqueryPlatform[];
+] as QueryableDisplayPlatform[];
const ERROR_NO_COMPATIBLE_TABLES = Error("no tables in query");
const formatPlatformsForDisplay = (
- compatiblePlatforms: OsqueryPlatform[]
-): OsqueryPlatform[] => {
+ compatiblePlatforms: QueryablePlatform[]
+): DisplayPlatform[] => {
return compatiblePlatforms.map((str) => PLATFORM_DISPLAY_NAMES[str] || str);
};
@@ -83,8 +87,9 @@ const PlatformCompatibility = ({
- Estimated compatiblity based on
the tables used in the
- query.
+ Estimated compatibility based on the
+ tables used in the query. Querying
+ iPhones & iPads is not supported.
>
}
>
diff --git a/frontend/components/TableContainer/DataTable/PlatformCell/PlatformCell.tests.tsx b/frontend/components/TableContainer/DataTable/PlatformCell/PlatformCell.tests.tsx
index 630eb461043e..9f91b32ee384 100644
--- a/frontend/components/TableContainer/DataTable/PlatformCell/PlatformCell.tests.tsx
+++ b/frontend/components/TableContainer/DataTable/PlatformCell/PlatformCell.tests.tsx
@@ -2,10 +2,10 @@ import React from "react";
import { render, screen } from "@testing-library/react";
import { DEFAULT_EMPTY_CELL_VALUE } from "utilities/constants";
-import { SupportedPlatform } from "interfaces/platform";
+import { QueryablePlatform } from "interfaces/platform";
import PlatformCell from "./PlatformCell";
-const PLATFORMS: SupportedPlatform[] = ["windows", "darwin", "linux", "chrome"];
+const PLATFORMS: QueryablePlatform[] = ["windows", "darwin", "linux", "chrome"];
describe("Platform cell", () => {
it("renders platform icons in correct order", () => {
diff --git a/frontend/components/TableContainer/DataTable/PlatformCell/PlatformCell.tsx b/frontend/components/TableContainer/DataTable/PlatformCell/PlatformCell.tsx
index 48665040c643..f88026663840 100644
--- a/frontend/components/TableContainer/DataTable/PlatformCell/PlatformCell.tsx
+++ b/frontend/components/TableContainer/DataTable/PlatformCell/PlatformCell.tsx
@@ -1,22 +1,22 @@
import React from "react";
import Icon from "components/Icon";
-import { SupportedPlatform } from "interfaces/platform";
+import { QueryablePlatform } from "interfaces/platform";
import { DEFAULT_EMPTY_CELL_VALUE } from "utilities/constants";
interface IPlatformCellProps {
- platforms: SupportedPlatform[];
+ platforms: QueryablePlatform[];
}
const baseClass = "platform-cell";
-const ICONS: Record = {
+const ICONS: Record = {
darwin: "darwin",
windows: "windows",
linux: "linux",
chrome: "chrome",
};
-const DISPLAY_ORDER: SupportedPlatform[] = [
+const DISPLAY_ORDER: QueryablePlatform[] = [
"darwin",
"windows",
"linux",
diff --git a/frontend/components/buttons/Button/Button.tsx b/frontend/components/buttons/Button/Button.tsx
index 03749d419b2a..3e05fda3fecf 100644
--- a/frontend/components/buttons/Button/Button.tsx
+++ b/frontend/components/buttons/Button/Button.tsx
@@ -13,7 +13,7 @@ export type ButtonVariant =
| "warning"
| "link"
| "label"
- | "text-link"
+ | "text-link" // Underlines on hover
| "text-icon"
| "icon" // Buttons without text
| "small-icon" // Buttons without text
diff --git a/frontend/components/icons/iOS.tsx b/frontend/components/icons/iOS.tsx
new file mode 100644
index 000000000000..a6fa41354a02
--- /dev/null
+++ b/frontend/components/icons/iOS.tsx
@@ -0,0 +1,42 @@
+import React from "react";
+import { COLORS, Colors } from "styles/var/colors";
+import { ICON_SIZES, IconSizes } from "styles/var/icon_sizes";
+
+interface IiOSProps {
+ size: IconSizes;
+ color?: Colors;
+}
+
+const iOS = ({ size = "medium", color = "ui-fleet-black-75" }: IiOSProps) => {
+ return (
+
+ );
+};
+
+export default iOS;
diff --git a/frontend/components/icons/iPadOS.tsx b/frontend/components/icons/iPadOS.tsx
new file mode 100644
index 000000000000..25b654fac0d9
--- /dev/null
+++ b/frontend/components/icons/iPadOS.tsx
@@ -0,0 +1,41 @@
+import React from "react";
+import { COLORS, Colors } from "styles/var/colors";
+import { ICON_SIZES, IconSizes } from "styles/var/icon_sizes";
+
+interface IiPadOSProps {
+ size: IconSizes;
+ color?: Colors;
+}
+
+const iPadOS = ({
+ size = "medium",
+ color = "ui-fleet-black-75",
+}: IiPadOSProps) => {
+ return (
+
+ );
+};
+
+export default iPadOS;
diff --git a/frontend/components/icons/index.ts b/frontend/components/icons/index.ts
index 3b573cf9ac48..d9cbb63a8f7d 100644
--- a/frontend/components/icons/index.ts
+++ b/frontend/components/icons/index.ts
@@ -34,6 +34,8 @@ import M1 from "./M1";
import Centos from "./Centos";
import Ubuntu from "./Ubuntu";
import Chrome from "./Chrome";
+import iPadOS from "./iPadOS";
+import iOS from "./iOS";
// Status Icons
import Success from "./Success";
@@ -114,6 +116,10 @@ export const ICON_MAP = {
ubuntu: Ubuntu,
chrome: Chrome,
ChromeOS: Chrome,
+ ipados: iPadOS,
+ iPadOS,
+ ios: iOS,
+ iOS,
"premium-feature": PremiumFeature,
profile: Profile,
download: Download,
diff --git a/frontend/components/side_panels/QuerySidePanel/QueryTableColumns/ColumnListItem/ColumnListItem.tsx b/frontend/components/side_panels/QuerySidePanel/QueryTableColumns/ColumnListItem/ColumnListItem.tsx
index fa67493864f4..8a061ca4a92d 100644
--- a/frontend/components/side_panels/QuerySidePanel/QueryTableColumns/ColumnListItem/ColumnListItem.tsx
+++ b/frontend/components/side_panels/QuerySidePanel/QueryTableColumns/ColumnListItem/ColumnListItem.tsx
@@ -1,10 +1,13 @@
import React from "react";
import classnames from "classnames";
-import { ColumnType, IQueryTableColumn } from "interfaces/osquery_table";
+import {
+ ColumnType,
+ IQueryTableColumn,
+ TableSchemaPlatform,
+} from "interfaces/osquery_table";
import TooltipWrapper from "components/TooltipWrapper";
import { buildQueryStringFromParams } from "utilities/url";
-import { OsqueryPlatform } from "interfaces/platform";
interface IColumnListItemProps {
column: IQueryTableColumn;
@@ -55,7 +58,7 @@ const renderTooltip = (
);
};
- const renderPlatformFootnotes = (columnPlatforms: OsqueryPlatform[]) => {
+ const renderPlatformFootnotes = (columnPlatforms: TableSchemaPlatform[]) => {
let platformsCopy;
switch (columnPlatforms.length) {
case 1:
diff --git a/frontend/components/side_panels/QuerySidePanel/QueryTablePlatforms/QueryTablePlatforms.tsx b/frontend/components/side_panels/QuerySidePanel/QueryTablePlatforms/QueryTablePlatforms.tsx
index c517e213505c..7b63f253d6a7 100644
--- a/frontend/components/side_panels/QuerySidePanel/QueryTablePlatforms/QueryTablePlatforms.tsx
+++ b/frontend/components/side_panels/QuerySidePanel/QueryTablePlatforms/QueryTablePlatforms.tsx
@@ -1,11 +1,11 @@
import React from "react";
-import { OsqueryPlatform } from "interfaces/platform";
import { PLATFORM_DISPLAY_NAMES } from "utilities/constants";
import Icon from "components/Icon";
+import { TableSchemaPlatform } from "interfaces/osquery_table";
interface IPLatformListItemProps {
- platform: OsqueryPlatform;
+ platform: TableSchemaPlatform;
}
const baseClassListItem = "platform-list-item";
@@ -20,7 +20,7 @@ const PlatformListItem = ({ platform }: IPLatformListItemProps) => {
};
// TODO: remove when freebsd is removed
-type IPlatformsWithFreebsd = OsqueryPlatform | "freebsd";
+type IPlatformsWithFreebsd = TableSchemaPlatform | "freebsd";
interface IQueryTablePlatformsProps {
platforms: IPlatformsWithFreebsd[];
@@ -38,7 +38,7 @@ const QueryTablePlatforms = ({ platforms }: IQueryTablePlatformsProps) => {
return (
);
});
diff --git a/frontend/hooks/usePlatformCompatibility.tsx b/frontend/hooks/usePlatformCompatibility.tsx
index 15fa948d33d7..ebb4ec1ee4e1 100644
--- a/frontend/hooks/usePlatformCompatibility.tsx
+++ b/frontend/hooks/usePlatformCompatibility.tsx
@@ -1,13 +1,13 @@
import React, { useCallback, useState } from "react";
import { useDebouncedCallback } from "use-debounce";
-import { OsqueryPlatform, SUPPORTED_PLATFORMS } from "interfaces/platform";
+import { QueryablePlatform, SUPPORTED_PLATFORMS } from "interfaces/platform";
import { checkPlatformCompatibility } from "utilities/sql_tools";
import PlatformCompatibility from "components/PlatformCompatibility";
export interface IPlatformCompatibility {
- getCompatiblePlatforms: () => ("darwin" | "windows" | "linux" | "chrome")[];
+ getCompatiblePlatforms: () => QueryablePlatform[];
setCompatiblePlatforms: (sqlString: string) => void;
render: () => JSX.Element;
}
@@ -16,7 +16,7 @@ const DEBOUNCE_DELAY = 300;
const usePlatformCompatibility = (): IPlatformCompatibility => {
const [compatiblePlatforms, setCompatiblePlatforms] = useState<
- OsqueryPlatform[] | null
+ QueryablePlatform[] | null
>(null);
const [error, setError] = useState(null);
diff --git a/frontend/hooks/usePlatformSelector.tsx b/frontend/hooks/usePlatformSelector.tsx
index 0583f16ec37f..b1fb384dd98f 100644
--- a/frontend/hooks/usePlatformSelector.tsx
+++ b/frontend/hooks/usePlatformSelector.tsx
@@ -4,13 +4,14 @@ import { forEach } from "lodash";
import {
SelectedPlatformString,
SUPPORTED_PLATFORMS,
+ QueryablePlatform,
} from "interfaces/platform";
import PlatformSelector from "components/PlatformSelector";
export interface IPlatformSelector {
setSelectedPlatforms: (platforms: string[]) => void;
- getSelectedPlatforms: () => ("darwin" | "windows" | "linux" | "chrome")[];
+ getSelectedPlatforms: () => QueryablePlatform[];
isAnyPlatformSelected: boolean;
render: () => JSX.Element;
disabled?: boolean;
diff --git a/frontend/interfaces/mdm.ts b/frontend/interfaces/mdm.ts
index a911f0c9e78b..3a75fc825866 100644
--- a/frontend/interfaces/mdm.ts
+++ b/frontend/interfaces/mdm.ts
@@ -67,7 +67,7 @@ export interface IMdmSummaryResponse {
mobile_device_management_solution: IMdmSummaryMdmSolution[] | null;
}
-export type ProfilePlatform = "darwin" | "windows";
+export type ProfilePlatform = "darwin" | "windows" | "ios" | "ipados";
export interface IProfileLabel {
name: string;
diff --git a/frontend/interfaces/osquery_table.ts b/frontend/interfaces/osquery_table.ts
index 3befc8f40305..c61faecba79d 100644
--- a/frontend/interfaces/osquery_table.ts
+++ b/frontend/interfaces/osquery_table.ts
@@ -1,5 +1,5 @@
import PropTypes from "prop-types";
-import { OsqueryPlatform } from "./platform";
+import { QueryablePlatform, QueryableDisplayPlatform } from "./platform";
export default PropTypes.shape({
columns: PropTypes.arrayOf(
@@ -23,6 +23,8 @@ export type ColumnType =
| "STRING"
| "string"; // TODO: Why do we have type string, STRING, and text in schema.json?
+// TODO: Replace with one or the other once osquery_fleet_schema.json follows one type or other
+export type TableSchemaPlatform = QueryableDisplayPlatform | QueryablePlatform;
export interface IQueryTableColumn {
name: string;
description: string;
@@ -30,7 +32,7 @@ export interface IQueryTableColumn {
hidden: boolean;
required: boolean;
index: boolean;
- platforms?: OsqueryPlatform[];
+ platforms?: TableSchemaPlatform[];
requires_user_context?: boolean;
}
@@ -38,7 +40,7 @@ export interface IOsQueryTable {
name: string;
description: string;
url: string;
- platforms: OsqueryPlatform[];
+ platforms: TableSchemaPlatform[];
evented: boolean;
cacheable: boolean;
columns: IQueryTableColumn[];
diff --git a/frontend/interfaces/platform.ts b/frontend/interfaces/platform.ts
index ec2e11efd3a4..5c9a9042cdd8 100644
--- a/frontend/interfaces/platform.ts
+++ b/frontend/interfaces/platform.ts
@@ -1,32 +1,44 @@
-export type OsqueryPlatform =
- | "darwin"
+export type DisplayPlatform =
| "macOS"
- | "windows"
| "Windows"
- | "linux"
| "Linux"
+ | "ChromeOS"
+ | "iOS"
+ | "iPadOS";
+
+export type Platform =
+ | "darwin"
+ | "windows"
+ | "linux"
| "chrome"
- | "ChromeOS";
+ | "ios"
+ | "ipados";
-export type SupportedPlatform = "darwin" | "windows" | "linux" | "chrome";
+export type QueryableDisplayPlatform = Exclude<
+ DisplayPlatform,
+ "iOS" | "iPadOS"
+>;
-export const SUPPORTED_PLATFORMS: SupportedPlatform[] = [
+export type QueryablePlatform = Exclude;
+
+export const SUPPORTED_PLATFORMS: QueryablePlatform[] = [
"darwin",
"windows",
"linux",
"chrome",
];
-export type SelectedPlatform = SupportedPlatform | "all";
+
+export type SelectedPlatform = QueryablePlatform | "all";
export type SelectedPlatformString =
| ""
- | SupportedPlatform
- | `${SupportedPlatform},${SupportedPlatform}`
- | `${SupportedPlatform},${SupportedPlatform},${SupportedPlatform}`
- | `${SupportedPlatform},${SupportedPlatform},${SupportedPlatform},${SupportedPlatform}`;
+ | QueryablePlatform
+ | `${QueryablePlatform},${QueryablePlatform}`
+ | `${QueryablePlatform},${QueryablePlatform},${QueryablePlatform}`
+ | `${QueryablePlatform},${QueryablePlatform},${QueryablePlatform},${QueryablePlatform}`;
// TODO: revisit this approach pending resolution of https://github.com/fleetdm/fleet/issues/3555.
-export const MACADMINS_EXTENSION_TABLES: Record = {
+export const MACADMINS_EXTENSION_TABLES: Record = {
file_lines: ["darwin", "linux", "windows"],
filevault_users: ["darwin"],
google_chrome_profiles: ["darwin", "linux", "windows"],
diff --git a/frontend/interfaces/schedulable_query.ts b/frontend/interfaces/schedulable_query.ts
index 89d56dc72699..23d2a9cd27d0 100644
--- a/frontend/interfaces/schedulable_query.ts
+++ b/frontend/interfaces/schedulable_query.ts
@@ -3,7 +3,7 @@ import PropTypes from "prop-types";
import { IFormField } from "./form_field";
import { IPack } from "./pack";
-import { SelectedPlatformString, SupportedPlatform } from "./platform";
+import { SelectedPlatformString, QueryablePlatform } from "./platform";
// Query itself
export interface ISchedulableQuery {
@@ -32,7 +32,7 @@ export interface ISchedulableQuery {
export interface IEnhancedQuery extends ISchedulableQuery {
performance: string;
- platforms: SupportedPlatform[];
+ platforms: QueryablePlatform[];
}
export interface ISchedulableQueryStats {
user_time_p50?: number | null;
diff --git a/frontend/pages/DashboardPage/DashboardPage.tsx b/frontend/pages/DashboardPage/DashboardPage.tsx
index 8a38f7a68757..321ba4c2c7db 100644
--- a/frontend/pages/DashboardPage/DashboardPage.tsx
+++ b/frontend/pages/DashboardPage/DashboardPage.tsx
@@ -29,7 +29,6 @@ import {
IMdmSummaryResponse,
IMdmSummaryMdmSolution,
} from "interfaces/mdm";
-import { SelectedPlatform } from "interfaces/platform";
import { ISoftwareResponse, ISoftwareCountResponse } from "interfaces/software";
import { API_ALL_TEAMS_ID, ITeam } from "interfaces/team";
import { IConfig } from "interfaces/config";
@@ -50,8 +49,7 @@ import hosts from "services/entities/hosts";
import sortUtils from "utilities/sort";
import {
DEFAULT_USE_QUERY_OPTIONS,
- PLATFORM_DROPDOWN_OPTIONS,
- PLATFORM_NAME_TO_LABEL_NAME,
+ PlatformValueOptions,
} from "utilities/constants";
import { ITableQueryData } from "components/TableContainer/TableContainer";
@@ -64,6 +62,10 @@ import Dropdown from "components/forms/fields/Dropdown";
import MainContent from "components/MainContent";
import LastUpdatedText from "components/LastUpdatedText";
+import {
+ PLATFORM_DROPDOWN_OPTIONS,
+ PLATFORM_NAME_TO_LABEL_NAME,
+} from "./helpers";
import useInfoCard from "./components/InfoCard";
import MissingHosts from "./cards/MissingHosts";
import LowDiskSpaceHosts from "./cards/LowDiskSpaceHosts";
@@ -124,9 +126,10 @@ const DashboardPage = ({ router, location }: IDashboardProps): JSX.Element => {
includeNoTeam: false,
});
- const [selectedPlatform, setSelectedPlatform] = useState(
- "all"
- );
+ const [
+ selectedPlatform,
+ setSelectedPlatform,
+ ] = useState("all");
const [
selectedPlatformLabelId,
setSelectedPlatformLabelId,
@@ -136,6 +139,8 @@ const DashboardPage = ({ router, location }: IDashboardProps): JSX.Element => {
const [windowsCount, setWindowsCount] = useState(0);
const [linuxCount, setLinuxCount] = useState(0);
const [chromeCount, setChromeCount] = useState(0);
+ const [iosCount, setIosCount] = useState(0);
+ const [ipadosCount, setIpadosCount] = useState(0);
const [missingCount, setMissingCount] = useState(0);
const [lowDiskSpaceCount, setLowDiskSpaceCount] = useState(0);
const [showActivityFeedTitle, setShowActivityFeedTitle] = useState(false);
@@ -243,10 +248,20 @@ const DashboardPage = ({ router, location }: IDashboardProps): JSX.Element => {
(platform: IHostSummaryPlatforms) => platform.platform === "chrome"
) || { platform: "chrome", hosts_count: 0 };
+ const iphones = data.platforms?.find(
+ (platform: IHostSummaryPlatforms) => platform.platform === "ios"
+ ) || { platform: "ios", hosts_count: 0 };
+
+ const ipads = data.platforms?.find(
+ (platform: IHostSummaryPlatforms) => platform.platform === "ipados"
+ ) || { platform: "ipados", hosts_count: 0 };
+
setMacCount(macHosts.hosts_count);
setWindowsCount(windowsHosts.hosts_count);
setLinuxCount(data.all_linux_count);
setChromeCount(chromebooks.hosts_count);
+ setIosCount(iphones.hosts_count);
+ setIpadosCount(ipads.hosts_count);
setShowHostsUI(true);
},
}
@@ -551,6 +566,8 @@ const DashboardPage = ({ router, location }: IDashboardProps): JSX.Element => {
windowsCount={windowsCount}
linuxCount={linuxCount}
chromeCount={chromeCount}
+ iosCount={iosCount}
+ ipadosCount={ipadosCount}
isLoadingHostsSummary={isHostSummaryFetching}
builtInLabels={labels}
showHostsUI={showHostsUI}
@@ -756,6 +773,20 @@ const DashboardPage = ({ router, location }: IDashboardProps): JSX.Element => {
>
);
+ const iosLayout = () => (
+ <>
+ {OperatingSystemsCard}
+ {showMdmCard && {MDMCard}
}
+ >
+ );
+
+ const ipadosLayout = () => (
+ <>
+ {OperatingSystemsCard}
+ {showMdmCard && {MDMCard}
}
+ >
+ );
+
const renderCards = () => {
switch (selectedPlatform) {
case "darwin":
@@ -766,6 +797,10 @@ const DashboardPage = ({ router, location }: IDashboardProps): JSX.Element => {
return linuxLayout();
case "chrome":
return chromeLayout();
+ case "ios":
+ return iosLayout();
+ case "ipados":
+ return ipadosLayout();
default:
return allLayout();
}
@@ -850,7 +885,7 @@ const DashboardPage = ({ router, location }: IDashboardProps): JSX.Element => {
className={`${baseClass}__platform_dropdown`}
options={PLATFORM_DROPDOWN_OPTIONS}
searchable={false}
- onChange={(value: SelectedPlatform) => {
+ onChange={(value: PlatformValueOptions) => {
const selectedPlatformOption = PLATFORM_DROPDOWN_OPTIONS.find(
(platform) => platform.value === value
);
@@ -870,12 +905,14 @@ const DashboardPage = ({ router, location }: IDashboardProps): JSX.Element => {
)}
{HostsSummaryCard}
- {isPremiumTier && (
-
- {MissingHostsCard}
- {LowDiskSpaceHostsCard}
-
- )}
+ {isPremiumTier &&
+ selectedPlatform !== "ios" &&
+ selectedPlatform !== "ipados" && (
+
+ {MissingHostsCard}
+ {LowDiskSpaceHostsCard}
+
+ )}
>
{renderCards()}
diff --git a/frontend/pages/DashboardPage/cards/HostsSummary/HostsSummary.tsx b/frontend/pages/DashboardPage/cards/HostsSummary/HostsSummary.tsx
index e7115b0f1831..6b9ced88ed96 100644
--- a/frontend/pages/DashboardPage/cards/HostsSummary/HostsSummary.tsx
+++ b/frontend/pages/DashboardPage/cards/HostsSummary/HostsSummary.tsx
@@ -1,10 +1,10 @@
-import React from "react";
+import React, { useCallback } from "react";
import PATHS from "router/paths";
-import { PLATFORM_NAME_TO_LABEL_NAME } from "utilities/constants";
+import { PLATFORM_NAME_TO_LABEL_NAME } from "pages/DashboardPage/helpers";
import DataError from "components/DataError";
-import { SelectedPlatform } from "interfaces/platform";
import { IHostSummary } from "interfaces/host_summary";
+import { PlatformValueOptions } from "utilities/constants";
import SummaryTile from "./SummaryTile";
@@ -16,11 +16,13 @@ interface IHostSummaryProps {
windowsCount: number;
linuxCount: number;
chromeCount: number;
+ iosCount: number;
+ ipadosCount: number;
isLoadingHostsSummary: boolean;
builtInLabels?: IHostSummary["builtin_labels"];
showHostsUI: boolean;
errorHosts: boolean;
- selectedPlatform?: SelectedPlatform;
+ selectedPlatform?: PlatformValueOptions;
}
const HostsSummary = ({
@@ -29,6 +31,8 @@ const HostsSummary = ({
windowsCount,
linuxCount,
chromeCount,
+ iosCount,
+ ipadosCount,
isLoadingHostsSummary,
builtInLabels,
showHostsUI,
@@ -41,10 +45,16 @@ const HostsSummary = ({
opacity = isLoadingHostsSummary ? { opacity: 0.4 } : { opacity: 1 };
}
+ const getBuiltinLabelId = useCallback(
+ (platformName: keyof typeof PLATFORM_NAME_TO_LABEL_NAME) =>
+ builtInLabels?.find(
+ (builtin) => builtin.name === PLATFORM_NAME_TO_LABEL_NAME[platformName]
+ )?.id,
+ [builtInLabels]
+ );
+
const renderMacCount = (teamId?: number) => {
- const macLabelId = builtInLabels?.find((builtin) => {
- return builtin.name === PLATFORM_NAME_TO_LABEL_NAME.darwin;
- })?.id;
+ const macLabelId = getBuiltinLabelId("darwin");
if (isLoadingHostsSummary || macLabelId === undefined) {
return <>>;
@@ -53,7 +63,6 @@ const HostsSummary = ({
return (
{
- const windowsLabelId = builtInLabels?.find(
- (builtin) => builtin.name === PLATFORM_NAME_TO_LABEL_NAME.windows
- )?.id;
+ const windowsLabelId = getBuiltinLabelId("windows");
if (isLoadingHostsSummary || windowsLabelId === undefined) {
return <>>;
@@ -76,7 +83,6 @@ const HostsSummary = ({
return (
{
- const linuxLabelId = builtInLabels?.find(
- (builtin) => builtin.name === PLATFORM_NAME_TO_LABEL_NAME.linux
- )?.id;
+ const linuxLabelId = getBuiltinLabelId("linux");
if (isLoadingHostsSummary || linuxLabelId === undefined) {
return <>>;
@@ -99,7 +103,6 @@ const HostsSummary = ({
return (
{
- const chromeLabelId = builtInLabels?.find(
- (builtin) => builtin.name === PLATFORM_NAME_TO_LABEL_NAME.chrome
- )?.id;
+ const chromeLabelId = getBuiltinLabelId("chrome");
if (isLoadingHostsSummary || chromeLabelId === undefined) {
return <>>;
@@ -123,7 +124,6 @@ const HostsSummary = ({
return (
{
+ const iosLabelId = getBuiltinLabelId("ios");
+
+ if (isLoadingHostsSummary || iosLabelId === undefined) {
+ return <>>;
+ }
+
+ return (
+
+ );
+ };
+
+ const renderIpadosCount = (teamId?: number) => {
+ const ipadosLabelId = getBuiltinLabelId("ipados");
+
+ if (isLoadingHostsSummary || ipadosLabelId === undefined) {
+ return <>>;
+ }
+
+ return (
+
+ );
+ };
+
const renderCounts = (teamId?: number) => {
switch (selectedPlatform) {
case "darwin":
@@ -145,6 +187,10 @@ const HostsSummary = ({
return renderLinuxCount(teamId);
case "chrome":
return renderChromeCount(teamId);
+ case "ios":
+ return renderIosCount(teamId);
+ case "ipados":
+ return renderIpadosCount(teamId);
default:
return (
<>
@@ -152,6 +198,8 @@ const HostsSummary = ({
{renderWindowsCount(teamId)}
{renderLinuxCount(teamId)}
{renderChromeCount(teamId)}
+ {renderIosCount(teamId)}
+ {renderIpadosCount(teamId)}
>
);
}
diff --git a/frontend/pages/DashboardPage/cards/HostsSummary/SummaryTile/SummaryTile.tests.tsx b/frontend/pages/DashboardPage/cards/HostsSummary/SummaryTile/SummaryTile.tests.tsx
index bb2689d948c7..cbbae35b2814 100644
--- a/frontend/pages/DashboardPage/cards/HostsSummary/SummaryTile/SummaryTile.tests.tsx
+++ b/frontend/pages/DashboardPage/cards/HostsSummary/SummaryTile/SummaryTile.tests.tsx
@@ -1,7 +1,6 @@
import React from "react";
import { fireEvent, render, screen } from "@testing-library/react";
-import { renderWithSetup } from "test/test-utils";
import paths from "router/paths";
import SummaryTile from "./SummaryTile";
@@ -16,7 +15,6 @@ describe("SummaryTile - component", () => {
showUI={false} // tested
title="Windows hosts"
iconName="windows"
- circledIcon
tooltip="Hosts on any Windows device"
path={paths.MANAGE_HOSTS_LABEL(10)}
/>
@@ -35,7 +33,6 @@ describe("SummaryTile - component", () => {
showUI
title="Windows hosts"
iconName="windows"
- circledIcon
tooltip="Hosts on any Windows device"
path={paths.MANAGE_HOSTS_LABEL(10)}
/>
@@ -55,7 +52,6 @@ describe("SummaryTile - component", () => {
showUI
title="Windows hosts" // tested
iconName="windows" // tested
- circledIcon
tooltip="Hosts on any Windows device"
path={paths.MANAGE_HOSTS_LABEL(10)}
/>
@@ -78,7 +74,6 @@ describe("SummaryTile - component", () => {
showUI
title="Windows hosts"
iconName="windows"
- circledIcon
path={paths.MANAGE_HOSTS_LABEL(10)}
/>
);
@@ -89,14 +84,13 @@ describe("SummaryTile - component", () => {
});
it("renders tooltip on title hover", async () => {
- const { user } = renderWithSetup(
+ render(
diff --git a/frontend/pages/DashboardPage/cards/HostsSummary/SummaryTile/SummaryTile.tsx b/frontend/pages/DashboardPage/cards/HostsSummary/SummaryTile/SummaryTile.tsx
index 55e5954ab3a7..697d542fb89e 100644
--- a/frontend/pages/DashboardPage/cards/HostsSummary/SummaryTile/SummaryTile.tsx
+++ b/frontend/pages/DashboardPage/cards/HostsSummary/SummaryTile/SummaryTile.tsx
@@ -4,7 +4,6 @@ import { kebabCase } from "lodash";
import Icon from "components/Icon";
import { IconNames } from "components/icons";
-import PremiumFeatureIconWithTooltip from "components/PremiumFeatureIconWithTooltip";
import classnames from "classnames";
import { Colors } from "styles/var/colors";
import TooltipWrapper from "components/TooltipWrapper";
@@ -15,12 +14,9 @@ interface ISummaryTileProps {
showUI: boolean;
title: string;
iconName: IconNames;
- circledIcon?: boolean;
iconColor?: Colors;
path: string;
tooltip?: string;
- isSandboxMode?: boolean;
- sandboxPremiumOnlyIcon?: boolean;
notSupported?: boolean;
}
@@ -32,12 +28,9 @@ const SummaryTile = ({
showUI, // false on first load only
title,
iconName,
- circledIcon,
iconColor,
path,
tooltip,
- isSandboxMode = false,
- sandboxPremiumOnlyIcon = false,
notSupported = false,
}: ISummaryTileProps): JSX.Element => {
const numberWithCommas = (x: number): string => {
@@ -54,15 +47,13 @@ const SummaryTile = ({
});
const tile = (
<>
-
+
-
-
{notSupported ? (
Not supported
@@ -76,18 +67,13 @@ const SummaryTile = ({
{numberWithCommas(count)}
)}
-
- {tooltip ? (
-
{title}
- ) : (
- title
- )}
- {isSandboxMode && sandboxPremiumOnlyIcon && (
-
- )}
-
+
+
+ {tooltip ? (
+ {title}
+ ) : (
+ title
+ )}
>
);
diff --git a/frontend/pages/DashboardPage/cards/HostsSummary/SummaryTile/_styles.scss b/frontend/pages/DashboardPage/cards/HostsSummary/SummaryTile/_styles.scss
index 6ab2f665963e..3b7b73e82458 100644
--- a/frontend/pages/DashboardPage/cards/HostsSummary/SummaryTile/_styles.scss
+++ b/frontend/pages/DashboardPage/cards/HostsSummary/SummaryTile/_styles.scss
@@ -1,5 +1,4 @@
.summary-tile {
- width: 100%;
display: flex;
justify-content: space-around;
@@ -12,6 +11,7 @@
&__tile {
display: flex;
+ flex-direction: column;
align-items: center;
gap: px-to-rem(12);
text-align: left;
@@ -27,20 +27,11 @@
white-space: nowrap;
}
- &__circled-icon {
+ &__icon-count {
display: flex;
- justify-content: center;
+ flex-direction: row;
align-items: center;
- background-color: $core-vibrant-blue;
- width: px-to-rem(48);
- height: px-to-rem(48);
- border-radius: 100%;
- background: $ui-vibrant-blue-10;
-
- .icon {
- margin-left: 0 !important;
- margin-right: 0;
- }
+ gap: 12px;
}
&__count {
@@ -57,7 +48,7 @@
}
&__description {
- white-space: nowrap;
+ color: $ui-fleet-black-75;
.component__tooltip-wrapper__element {
font-size: $x-small;
diff --git a/frontend/pages/DashboardPage/cards/HostsSummary/_styles.scss b/frontend/pages/DashboardPage/cards/HostsSummary/_styles.scss
index 896e7e3131ba..d1a532e0ea34 100644
--- a/frontend/pages/DashboardPage/cards/HostsSummary/_styles.scss
+++ b/frontend/pages/DashboardPage/cards/HostsSummary/_styles.scss
@@ -3,19 +3,17 @@
width: 100%;
display: flex;
justify-content: space-around;
+ flex-wrap: wrap;
font-size: $x-small;
+ gap: $pad-medium;
- &__tile {
- flex-grow: 1;
- display: flex;
- justify-content: center;
- align-items: center;
+ .summary-tile {
+ flex: 1 1 30%;
+ }
- &:first-of-type {
- justify-content: flex-end;
- }
- &:last-of-type {
- justify-content: flex-start;
+ @media (min-width: $break-md) {
+ .summary-tile {
+ flex: initial;
}
}
diff --git a/frontend/pages/DashboardPage/cards/LowDiskSpaceHosts/LowDiskSpaceHosts.tsx b/frontend/pages/DashboardPage/cards/LowDiskSpaceHosts/LowDiskSpaceHosts.tsx
index dadd27be467e..1ac6ebe4cd0e 100644
--- a/frontend/pages/DashboardPage/cards/LowDiskSpaceHosts/LowDiskSpaceHosts.tsx
+++ b/frontend/pages/DashboardPage/cards/LowDiskSpaceHosts/LowDiskSpaceHosts.tsx
@@ -14,7 +14,6 @@ interface IHostSummaryProps {
showHostsUI: boolean;
selectedPlatformLabelId?: number;
currentTeamId?: number;
- isSandboxMode?: boolean;
notSupported: boolean;
}
@@ -25,7 +24,6 @@ const LowDiskSpaceHosts = ({
showHostsUI,
selectedPlatformLabelId,
currentTeamId,
- isSandboxMode = false,
notSupported = false, // default to supporting this feature
}: IHostSummaryProps): JSX.Element => {
// build the manage hosts URL filtered by low disk space only
@@ -54,8 +52,6 @@ const LowDiskSpaceHosts = ({
title="Low disk space hosts"
tooltip={tooltipText}
path={path}
- isSandboxMode={isSandboxMode}
- sandboxPremiumOnlyIcon
notSupported={notSupported}
/>
diff --git a/frontend/pages/DashboardPage/cards/MissingHosts/MissingHosts.tsx b/frontend/pages/DashboardPage/cards/MissingHosts/MissingHosts.tsx
index 956b8a14fc48..2bb0e4201d1e 100644
--- a/frontend/pages/DashboardPage/cards/MissingHosts/MissingHosts.tsx
+++ b/frontend/pages/DashboardPage/cards/MissingHosts/MissingHosts.tsx
@@ -13,7 +13,6 @@ interface IHostSummaryProps {
showHostsUI: boolean;
selectedPlatformLabelId?: number;
currentTeamId?: number;
- isSandboxMode?: boolean;
}
const MissingHosts = ({
@@ -22,7 +21,6 @@ const MissingHosts = ({
showHostsUI,
selectedPlatformLabelId,
currentTeamId,
- isSandboxMode = false,
}: IHostSummaryProps): JSX.Element => {
// build the manage hosts URL filtered by missing and platform
const queryParams = {
@@ -45,8 +43,6 @@ const MissingHosts = ({
title="Missing hosts"
tooltip="Hosts that have not been online in 30 days or more."
path={path}
- isSandboxMode={isSandboxMode}
- sandboxPremiumOnlyIcon
/>
);
diff --git a/frontend/pages/DashboardPage/cards/OperatingSystems/OperatingSystems.tsx b/frontend/pages/DashboardPage/cards/OperatingSystems/OperatingSystems.tsx
index 86dbf573eb45..5d2ef7f5dafb 100644
--- a/frontend/pages/DashboardPage/cards/OperatingSystems/OperatingSystems.tsx
+++ b/frontend/pages/DashboardPage/cards/OperatingSystems/OperatingSystems.tsx
@@ -5,14 +5,16 @@ import {
OS_END_OF_LIFE_LINK_BY_PLATFORM,
OS_VENDOR_BY_PLATFORM,
} from "interfaces/operating_system";
-import { SelectedPlatform } from "interfaces/platform";
import {
getOSVersions,
IGetOSVersionsQueryKey,
IOSVersionsResponse,
OS_VERSIONS_API_SUPPORTED_PLATFORMS,
} from "services/entities/operating_systems";
-import { PLATFORM_DISPLAY_NAMES } from "utilities/constants";
+import {
+ PLATFORM_DISPLAY_NAMES,
+ PlatformValueOptions,
+} from "utilities/constants";
import TableContainer from "components/TableContainer";
import Spinner from "components/Spinner";
@@ -26,7 +28,7 @@ import generateTableHeaders from "./OperatingSystemsTableConfig";
interface IOperatingSystemsCardProps {
currentTeamId: number | undefined;
- selectedPlatform: SelectedPlatform;
+ selectedPlatform: PlatformValueOptions;
showTitle: boolean;
/** controls the displaying of description text under the title. Defaults to `true` */
showDescription?: boolean;
@@ -42,7 +44,7 @@ const DEFAULT_SORT_HEADER = "hosts_count";
const PAGE_SIZE = 8;
const baseClass = "operating-systems";
-const EmptyOperatingSystems = (platform: SelectedPlatform): JSX.Element => (
+const EmptyOperatingSystems = (platform: PlatformValueOptions): JSX.Element => (
{
const getPlatformName = () => {
if (platform === "windows") return "Windows";
- return isDDM ? "macOS (declaration)" : "macOS";
+ return isDDM ? "macOS (declaration)" : "macOS, iOS, iPadOS";
};
return (
@@ -57,7 +58,7 @@ const createProfileExtension = (profile: IMdmProfile) => {
if (isDDMProfile(profile)) {
return "json";
}
- return profile.platform === "darwin" ? "mobileconfig" : "xml";
+ return isAppleDevice(profile.platform) ? "mobileconfig" : "xml";
};
const createFileContent = async (profile: IMdmProfile) => {
diff --git a/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/ProfileListItem/_styles.scss b/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/ProfileListItem/_styles.scss
index 550e2a9daedc..95436510d152 100644
--- a/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/ProfileListItem/_styles.scss
+++ b/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/ProfileListItem/_styles.scss
@@ -3,6 +3,7 @@
display: flex;
flex-direction: row;
gap: $pad-xsmall;
+ color: $ui-fleet-black-75;
}
&__list-item-details {
diff --git a/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/ProfileUploader/components/AddProfileGraphic.tsx b/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/ProfileUploader/components/AddProfileGraphic.tsx
index c75193dcb6cd..027bf94f46b6 100644
--- a/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/ProfileUploader/components/AddProfileGraphic.tsx
+++ b/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/ProfileUploader/components/AddProfileGraphic.tsx
@@ -2,9 +2,6 @@ import React from "react";
import Graphic from "components/Graphic";
-const ALLOWED_FILE_TYPES_MESSAGE =
- "Configuration profile (.mobileconfig and .json for macOS or .xml for Windows)";
-
const ProfileGraphic = ({
baseClass,
showMessage,
@@ -14,13 +11,17 @@ const ProfileGraphic = ({
}) => (
{showMessage && (
- {ALLOWED_FILE_TYPES_MESSAGE}
+ Upload configuration profile
+
+ .mobileconfig and .json for macOS, iOS, and iPadOS.
+
+ .xml for Windows.
)}
diff --git a/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/ProfileUploader/components/AddProfileModal/helpers.tsx b/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/ProfileUploader/components/AddProfileModal/helpers.tsx
index e8ff573ecefe..f1ed1acdd7b8 100644
--- a/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/ProfileUploader/components/AddProfileModal/helpers.tsx
+++ b/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/ProfileUploader/components/AddProfileModal/helpers.tsx
@@ -1,7 +1,6 @@
import React from "react";
import { IDropdownOption } from "interfaces/dropdownOption";
-import { snakeCase } from "lodash";
export const CUSTOM_TARGET_OPTIONS: IDropdownOption[] = [
{
diff --git a/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/ProfileUploader/helpers.tsx b/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/ProfileUploader/helpers.tsx
index 62727c9b2f84..4132a49a042a 100644
--- a/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/ProfileUploader/helpers.tsx
+++ b/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/ProfileUploader/helpers.tsx
@@ -13,10 +13,10 @@ export const parseFile = async (file: File): Promise<[string, string]> => {
return [name, "Windows"];
}
case "mobileconfig": {
- return [name, "macOS"];
+ return [name, "macOS, iOS, iPadOS"];
}
case "json": {
- return [name, "macOS"];
+ return [name, "macOS, iOS, iPadOS"];
}
default: {
throw new Error(`Invalid file type: ${ext}`);
diff --git a/frontend/pages/ManageControlsPage/OSUpdates/OSUpdates.tsx b/frontend/pages/ManageControlsPage/OSUpdates/OSUpdates.tsx
index 4e6d7ae31199..e18a4afca21e 100644
--- a/frontend/pages/ManageControlsPage/OSUpdates/OSUpdates.tsx
+++ b/frontend/pages/ManageControlsPage/OSUpdates/OSUpdates.tsx
@@ -18,7 +18,11 @@ import TurnOnMdmMessage from "../components/TurnOnMdmMessage/TurnOnMdmMessage";
import CurrentVersionSection from "./components/CurrentVersionSection";
import TargetSection from "./components/TargetSection";
-export type OSUpdatesSupportedPlatform = "darwin" | "windows";
+export type OSUpdatesSupportedPlatform =
+ | "darwin"
+ | "windows"
+ | "iOS"
+ | "iPadOS";
const baseClass = "os-updates";
@@ -89,6 +93,7 @@ const OSUpdates = ({ router, teamIdForApi }: IOSUpdates) => {
// FIXME: Handle error states for app config and team config (need specifications for this).
// mdm is not enabled for mac or windows.
+
if (
!config?.mdm.enabled_and_configured &&
!config?.mdm.windows_enabled_and_configured
diff --git a/frontend/pages/ManageControlsPage/OSUpdates/components/CurrentVersionSection/CurrentVersionSection.tsx b/frontend/pages/ManageControlsPage/OSUpdates/components/CurrentVersionSection/CurrentVersionSection.tsx
index f0c62ddc09bb..98ce7349ab73 100644
--- a/frontend/pages/ManageControlsPage/OSUpdates/components/CurrentVersionSection/CurrentVersionSection.tsx
+++ b/frontend/pages/ManageControlsPage/OSUpdates/components/CurrentVersionSection/CurrentVersionSection.tsx
@@ -79,10 +79,13 @@ const CurrentVersionSection = ({
return ;
}
- // We only want to show windows and mac versions atm.
+ // We only want to show windows mac, ios, ipados versions atm.
const filteredOSVersionData = data.os_versions.filter((osVersion) => {
return (
- osVersion.platform === "windows" || osVersion.platform === "darwin"
+ osVersion.platform === "windows" ||
+ osVersion.platform === "darwin" ||
+ osVersion.platform === "ios" ||
+ osVersion.platform === "ipados"
);
}) as IFilteredOperatingSystemVersion[];
diff --git a/frontend/pages/ManageControlsPage/OSUpdates/components/EmptyTargetForm/EmptyTargetForm.tsx b/frontend/pages/ManageControlsPage/OSUpdates/components/EmptyTargetForm/EmptyTargetForm.tsx
new file mode 100644
index 000000000000..80d24adc39a3
--- /dev/null
+++ b/frontend/pages/ManageControlsPage/OSUpdates/components/EmptyTargetForm/EmptyTargetForm.tsx
@@ -0,0 +1,29 @@
+import React from "react";
+
+import CustomLink from "components/CustomLink";
+
+const baseClass = "empty-target-form";
+
+interface IEmptyTargetFormProps {
+ targetPlatform: string;
+}
+
+const EmptyTargetForm = ({ targetPlatform }: IEmptyTargetFormProps) => {
+ return (
+
+
+ {targetPlatform} updates are coming soon.
+
+
+ Need to remotely encourage installation of {targetPlatform} updates?{" "}
+
+
+
+ );
+};
+
+export default EmptyTargetForm;
diff --git a/frontend/pages/ManageControlsPage/OSUpdates/components/EmptyTargetForm/index.ts b/frontend/pages/ManageControlsPage/OSUpdates/components/EmptyTargetForm/index.ts
new file mode 100644
index 000000000000..284c5dbd3a0a
--- /dev/null
+++ b/frontend/pages/ManageControlsPage/OSUpdates/components/EmptyTargetForm/index.ts
@@ -0,0 +1 @@
+export { default } from "./EmptyTargetForm";
diff --git a/frontend/pages/ManageControlsPage/OSUpdates/components/NudgePreview/NudgePreview.tsx b/frontend/pages/ManageControlsPage/OSUpdates/components/NudgePreview/NudgePreview.tsx
index 813689d2051f..9f75ee13bb90 100644
--- a/frontend/pages/ManageControlsPage/OSUpdates/components/NudgePreview/NudgePreview.tsx
+++ b/frontend/pages/ManageControlsPage/OSUpdates/components/NudgePreview/NudgePreview.tsx
@@ -64,6 +64,12 @@ interface INudgePreviewProps {
}
const NudgePreview = ({ platform }: INudgePreviewProps) => {
+ const isSupportedPlatform = platform === "windows" || platform === "darwin";
+
+ if (!isSupportedPlatform) {
+ return null;
+ }
+
// FIXME: on slow connection the image loads after the text which looks weird and can cause a
// mismatch between the text and the image when switching between platforms. We should load the
// image first and then the text.
diff --git a/frontend/pages/ManageControlsPage/OSUpdates/components/OSVersionTable/OSVersionTable.tsx b/frontend/pages/ManageControlsPage/OSUpdates/components/OSVersionTable/OSVersionTable.tsx
index a0b3ef06b465..372430e0c036 100644
--- a/frontend/pages/ManageControlsPage/OSUpdates/components/OSVersionTable/OSVersionTable.tsx
+++ b/frontend/pages/ManageControlsPage/OSUpdates/components/OSVersionTable/OSVersionTable.tsx
@@ -39,7 +39,8 @@ const OSVersionTable = ({
defaultSortDirection={DEFAULT_SORT_DIRECTION}
disableTableHeader
disableCount
- disablePagination
+ pageSize={8}
+ isClientSidePagination
/>
);
diff --git a/frontend/pages/ManageControlsPage/OSUpdates/components/PlatformTabs/PlatformTabs.tsx b/frontend/pages/ManageControlsPage/OSUpdates/components/PlatformTabs/PlatformTabs.tsx
index af5432a941f7..3befce43aab7 100644
--- a/frontend/pages/ManageControlsPage/OSUpdates/components/PlatformTabs/PlatformTabs.tsx
+++ b/frontend/pages/ManageControlsPage/OSUpdates/components/PlatformTabs/PlatformTabs.tsx
@@ -5,6 +5,7 @@ import TabsWrapper from "components/TabsWrapper";
import MacOSTargetForm from "../MacOSTargetForm";
import WindowsTargetForm from "../WindowsTargetForm";
import { OSUpdatesSupportedPlatform } from "../../OSUpdates";
+import EmptyTargetForm from "../EmptyTargetForm";
const baseClass = "platform-tabs";
@@ -33,18 +34,40 @@ const PlatformTabs = ({
}: IPlatformTabsProps) => {
// FIXME: This behaves unexpectedly when a user switches tabs or changes the teams dropdown while a form is
// submitting.
+
+ const PLATFORM_BY_INDEX: OSUpdatesSupportedPlatform[] = [
+ "darwin",
+ "windows",
+ "iOS",
+ "iPadOS",
+ ];
+
+ const onTabChange = (index: number) => {
+ onSelectPlatform(PLATFORM_BY_INDEX[index]);
+ };
+
return (
- onSelectPlatform(currentIndex === 0 ? "darwin" : "windows")
- }
+ defaultIndex={PLATFORM_BY_INDEX.indexOf(selectedPlatform)}
+ onSelect={onTabChange}
>
- macOS
- Windows
+ {/* Bolding text when the tab is active causes a layout shift so
+ we add a hidden pseudo element with the same text string */}
+
+ macOS
+
+
+ Windows
+
+
+ iOS
+
+
+ iPadOS
+
+
+
+
+
+
+
diff --git a/frontend/pages/ManageControlsPage/components/UploadList/_styles.scss b/frontend/pages/ManageControlsPage/components/UploadList/_styles.scss
index 0f5017b10260..6a7cb20cd9ed 100644
--- a/frontend/pages/ManageControlsPage/components/UploadList/_styles.scss
+++ b/frontend/pages/ManageControlsPage/components/UploadList/_styles.scss
@@ -1,8 +1,12 @@
.upload-list {
+ border: 1px solid $ui-fleet-black-10;
+ border-radius: 4px;
+
&__header {
padding: $pad-medium $pad-large;
font-size: $x-small;
border-bottom: 1px solid $ui-fleet-black-10;
+ background-color: $ui-off-white;
}
&__list {
@@ -14,5 +18,9 @@
&__list-item {
padding: $pad-medium $pad-large;
border-bottom: 1px solid $ui-fleet-black-10;
+
+ &:last-child {
+ border-bottom: initial;
+ }
}
}
diff --git a/frontend/pages/SoftwarePage/helpers.ts b/frontend/pages/SoftwarePage/helpers.ts
deleted file mode 100644
index e69de29bb2d1..000000000000
diff --git a/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/TeamDetailsWrapper.tsx b/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/TeamDetailsWrapper.tsx
index 367c76e113b1..90a3d609d929 100644
--- a/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/TeamDetailsWrapper.tsx
+++ b/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/TeamDetailsWrapper.tsx
@@ -465,11 +465,6 @@ const TeamDetailsWrapper = ({
enrollSecret={teamSecrets?.[0]?.secret}
isAnyTeamSelected={isAnyTeamSelected}
isLoading={isLoadingTeams}
- // TODO: Currently, prepacked installers in Fleet Sandbox use the global enroll secret,
- // and Fleet Sandbox runs Fleet Free so explicitly setting isSandboxMode here is an
- // additional precaution/reminder to revisit this in connection with future changes.
- // See https://github.com/fleetdm/fleet/issues/4970#issuecomment-1187679407.
- isSandboxMode={false}
onCancel={toggleAddHostsModal}
openEnrollSecretModal={toggleManageEnrollSecretsModal}
/>
diff --git a/frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx b/frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx
index 0c794a7cea83..d3d69af8d373 100644
--- a/frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx
+++ b/frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx
@@ -54,7 +54,7 @@ import {
isValidSoftwareInstallStatus,
SoftwareInstallStatus,
} from "interfaces/software";
-import { API_NO_TEAM_ID, ITeam } from "interfaces/team";
+import { ITeam } from "interfaces/team";
import { IEmptyTableProps } from "interfaces/empty_table";
import {
DiskEncryptionStatus,
@@ -1258,21 +1258,15 @@ const ManageHostsPage = ({
);
const renderAddHostsModal = () => {
- const enrollSecret =
- // TODO: Currently, prepacked installers in Fleet Sandbox use the global enroll secret,
- // and Fleet Sandbox runs Fleet Free so the isSandboxMode check here is an
- // additional precaution/reminder to revisit this in connection with future changes.
- // See https://github.com/fleetdm/fleet/issues/4970#issuecomment-1187679407.
- isAnyTeamSelected && !isSandboxMode
- ? teamSecrets?.[0].secret
- : globalSecrets?.[0].secret;
+ const enrollSecret = isAnyTeamSelected
+ ? teamSecrets?.[0].secret
+ : globalSecrets?.[0].secret;
return (
setShowEnrollSecretModal(true)}
/>
@@ -1734,7 +1728,6 @@ const ManageHostsPage = ({
}
onClickEditLabel={onEditLabelClick}
onClickDeleteLabel={toggleDeleteLabelModal}
- isSandboxMode={isSandboxMode}
/>
{renderNoEnrollSecretBanner()}
{renderTable()}
diff --git a/frontend/pages/hosts/ManageHostsPage/components/FilterPill/FilterPill.tsx b/frontend/pages/hosts/ManageHostsPage/components/FilterPill/FilterPill.tsx
index f44e71b6ca68..8f9fea2dfaf8 100644
--- a/frontend/pages/hosts/ManageHostsPage/components/FilterPill/FilterPill.tsx
+++ b/frontend/pages/hosts/ManageHostsPage/components/FilterPill/FilterPill.tsx
@@ -3,7 +3,6 @@ import ReactTooltip from "react-tooltip";
import classnames from "classnames";
import Button from "components/buttons/Button";
-import PremiumFeatureIconWithTooltip from "components/PremiumFeatureIconWithTooltip";
import Icon from "components/Icon";
import { IconNames } from "components/icons";
@@ -14,10 +13,7 @@ interface IFilterPillProps {
onClear: () => void;
icon?: IconNames;
tooltipDescription?: string | ReactNode;
- premiumFeatureTooltipDelayHide?: number;
className?: string;
- isSandboxMode?: boolean;
- sandboxPremiumOnlyIcon?: boolean;
}
const baseClass = "filter-pill";
@@ -26,11 +22,8 @@ const FilterPill = ({
label,
icon,
tooltipDescription,
- premiumFeatureTooltipDelayHide,
className,
onClear,
- isSandboxMode = false,
- sandboxPremiumOnlyIcon = false,
}: IFilterPillProps) => {
const baseClasses = classnames(baseClass, className);
const labelClasses = classnames(`${baseClass}__label`, {
@@ -47,12 +40,6 @@ const FilterPill = ({
{icon &&
}
- {isSandboxMode && sandboxPremiumOnlyIcon && (
-
- )}
void;
onClickEditLabel: (evt: React.MouseEvent) => void;
onClickDeleteLabel: () => void;
- isSandboxMode?: boolean;
}
/**
@@ -137,7 +136,6 @@ const HostsFilterBlock = ({
onChangeSoftwareInstallStatusFilter,
onClickEditLabel,
onClickDeleteLabel,
- isSandboxMode = false,
}: IHostsFilterBlockProps) => {
const renderLabelFilterPill = () => {
if (selectedLabel) {
@@ -402,10 +400,7 @@ const HostsFilterBlock = ({
handleClearFilter(["low_disk_space"])}
- isSandboxMode={isSandboxMode}
- sandboxPremiumOnlyIcon
/>
);
};
diff --git a/frontend/pages/hosts/details/HostDetailsPage/modals/DiskEncryptionKeyModal/DiskEncryptionKeyModal.tsx b/frontend/pages/hosts/details/HostDetailsPage/modals/DiskEncryptionKeyModal/DiskEncryptionKeyModal.tsx
index 17a276686e64..9dae6b37ab3c 100644
--- a/frontend/pages/hosts/details/HostDetailsPage/modals/DiskEncryptionKeyModal/DiskEncryptionKeyModal.tsx
+++ b/frontend/pages/hosts/details/HostDetailsPage/modals/DiskEncryptionKeyModal/DiskEncryptionKeyModal.tsx
@@ -8,14 +8,14 @@ import Modal from "components/Modal";
import Button from "components/buttons/Button";
import InputFieldHiddenContent from "components/forms/fields/InputFieldHiddenContent";
import DataError from "components/DataError";
-import { SupportedPlatform } from "interfaces/platform";
+import { QueryablePlatform } from "interfaces/platform";
const baseClass = "disk-encryption-key-modal";
// currently these are the only supported platforms for the disk encryption
// key modal.
export type ModalSupportedPlatform = Extract<
- SupportedPlatform,
+ QueryablePlatform,
"darwin" | "windows"
>;
diff --git a/frontend/pages/hosts/details/OSSettingsModal/OSSettingsTable/OSSettingsTableConfig.tsx b/frontend/pages/hosts/details/OSSettingsModal/OSSettingsTable/OSSettingsTableConfig.tsx
index badef6d0b790..197c8603ccca 100644
--- a/frontend/pages/hosts/details/OSSettingsModal/OSSettingsTable/OSSettingsTableConfig.tsx
+++ b/frontend/pages/hosts/details/OSSettingsModal/OSSettingsTable/OSSettingsTableConfig.tsx
@@ -146,6 +146,10 @@ export const generateTableData = (
return makeWindowsRows(hostMDMData);
case "darwin":
return makeDarwinRows(hostMDMData);
+ case "ios":
+ return hostMDMData.profiles;
+ case "ipados":
+ return hostMDMData.profiles;
default:
return null;
}
diff --git a/frontend/pages/hosts/details/cards/HostSummary/HostSummary.tsx b/frontend/pages/hosts/details/cards/HostSummary/HostSummary.tsx
index bcb632f7c127..146809ad5628 100644
--- a/frontend/pages/hosts/details/cards/HostSummary/HostSummary.tsx
+++ b/frontend/pages/hosts/details/cards/HostSummary/HostSummary.tsx
@@ -432,7 +432,10 @@ const HostSummary = ({
renderIssues()}
{isPremiumTier && renderHostTeam()}
{/* Rendering of OS Settings data */}
- {(platform === "darwin" || platform === "windows") &&
+ {(platform === "darwin" ||
+ platform === "windows" ||
+ platform === "ios" ||
+ platform === "ipados") &&
isPremiumTier &&
isConnectedToFleetMdm && // show if 1 - host is enrolled in Fleet MDM, and
hostMdmProfiles &&
diff --git a/frontend/pages/hosts/details/cards/HostSummary/OSSettingsIndicator/OSSettingsIndicator.tsx b/frontend/pages/hosts/details/cards/HostSummary/OSSettingsIndicator/OSSettingsIndicator.tsx
index 68043e52a7f2..bd129a20c5bb 100644
--- a/frontend/pages/hosts/details/cards/HostSummary/OSSettingsIndicator/OSSettingsIndicator.tsx
+++ b/frontend/pages/hosts/details/cards/HostSummary/OSSettingsIndicator/OSSettingsIndicator.tsx
@@ -31,9 +31,7 @@ type StatusDisplayOptions = Record<
const STATUS_DISPLAY_OPTIONS: StatusDisplayOptions = {
Verified: {
iconName: "success",
- tooltipText:
- "The host applied all OS settings. Fleet verified with osquery. " +
- "Declaration profiles are verified with DDM.",
+ tooltipText: "These hosts applied all OS settings. Fleet verified.",
},
Verifying: {
iconName: "success-outline",
diff --git a/frontend/pages/queries/ManageQueriesPage/ManageQueriesPage.tsx b/frontend/pages/queries/ManageQueriesPage/ManageQueriesPage.tsx
index dd4ed9ba99f2..f737f22cfd3b 100644
--- a/frontend/pages/queries/ManageQueriesPage/ManageQueriesPage.tsx
+++ b/frontend/pages/queries/ManageQueriesPage/ManageQueriesPage.tsx
@@ -14,7 +14,7 @@ import { QueryContext } from "context/query";
import { TableContext } from "context/table";
import { NotificationContext } from "context/notification";
import { getPerformanceImpactDescription } from "utilities/helpers";
-import { SupportedPlatform, SelectedPlatform } from "interfaces/platform";
+import { QueryablePlatform, SelectedPlatform } from "interfaces/platform";
import {
IEnhancedQuery,
IQueryKeyQueriesLoadAll,
@@ -54,7 +54,7 @@ interface IManageQueriesPageProps {
};
}
-const getPlatforms = (queryString: string): SupportedPlatform[] => {
+const getPlatforms = (queryString: string): QueryablePlatform[] => {
const { platforms } = checkPlatformCompatibility(queryString);
return platforms ?? [];
diff --git a/frontend/pages/queries/ManageQueriesPage/components/QueriesTable/QueriesTableConfig.tsx b/frontend/pages/queries/ManageQueriesPage/components/QueriesTable/QueriesTableConfig.tsx
index 81ff22dcf883..b38b394735aa 100644
--- a/frontend/pages/queries/ManageQueriesPage/components/QueriesTable/QueriesTableConfig.tsx
+++ b/frontend/pages/queries/ManageQueriesPage/components/QueriesTable/QueriesTableConfig.tsx
@@ -12,7 +12,7 @@ import {
IEnhancedQuery,
ISchedulableQuery,
} from "interfaces/schedulable_query";
-import { SupportedPlatform } from "interfaces/platform";
+import { QueryablePlatform } from "interfaces/platform";
import { API_ALL_TEAMS_ID } from "interfaces/team";
import Icon from "components/Icon";
@@ -81,7 +81,7 @@ interface IBoolCellProps extends IRowProps {
}
interface IPlatformCellProps extends IRowProps {
cell: {
- value: SupportedPlatform[];
+ value: QueryablePlatform[];
};
}
diff --git a/frontend/router/index.tsx b/frontend/router/index.tsx
index ae9931afb6e5..2d3c83a8d833 100644
--- a/frontend/router/index.tsx
+++ b/frontend/router/index.tsx
@@ -133,6 +133,8 @@ const routes = (
+
+
diff --git a/frontend/router/paths.ts b/frontend/router/paths.ts
index ae2b39b2fd67..0aedc5ee3029 100644
--- a/frontend/router/paths.ts
+++ b/frontend/router/paths.ts
@@ -23,6 +23,8 @@ export default {
DASHBOARD_MAC: `${URL_PREFIX}/dashboard/mac`,
DASHBOARD_WINDOWS: `${URL_PREFIX}/dashboard/windows`,
DASHBOARD_CHROME: `${URL_PREFIX}/dashboard/chrome`,
+ DASHBOARD_IOS: `${URL_PREFIX}/dashboard/ios`,
+ DASHBOARD_IPADOS: `${URL_PREFIX}/dashboard/ipados`,
// Admin pages
ADMIN_SETTINGS: `${URL_PREFIX}/settings`,
diff --git a/frontend/services/entities/hosts.ts b/frontend/services/entities/hosts.ts
index 259d9ae9a217..ba7e0dc7abf7 100644
--- a/frontend/services/entities/hosts.ts
+++ b/frontend/services/entities/hosts.ts
@@ -9,7 +9,6 @@ import {
reconcileMutuallyExclusiveHostParams,
reconcileMutuallyInclusiveHostParams,
} from "utilities/url";
-import { SelectedPlatform } from "interfaces/platform";
import {
IHostSoftware,
ISoftware,
@@ -23,7 +22,7 @@ import {
MdmEnrollmentStatus,
} from "interfaces/mdm";
import { IMunkiIssuesAggregate } from "interfaces/macadmins";
-import { PolicyResponse } from "utilities/constants";
+import { PlatformValueOptions, PolicyResponse } from "utilities/constants";
export interface ISortOption {
key: string;
@@ -206,7 +205,7 @@ const getSortParams = (sortOptions?: ISortOption[]) => {
};
};
-const createMdmParams = (platform?: SelectedPlatform, teamId?: number) => {
+const createMdmParams = (platform?: PlatformValueOptions, teamId?: number) => {
if (platform === "all") {
return buildQueryStringFromParams({ team_id: teamId });
}
@@ -535,7 +534,7 @@ export default {
return sendRequest("GET", HOST_MDM(id));
},
- getMdmSummary: (platform?: SelectedPlatform, teamId?: number) => {
+ getMdmSummary: (platform?: PlatformValueOptions, teamId?: number) => {
const { MDM_SUMMARY } = endpoints;
if (!platform || platform === "linux") {
diff --git a/frontend/services/entities/operating_systems.ts b/frontend/services/entities/operating_systems.ts
index 29ba06b14103..1611243bfd91 100644
--- a/frontend/services/entities/operating_systems.ts
+++ b/frontend/services/entities/operating_systems.ts
@@ -2,7 +2,7 @@
import sendRequest from "services";
import endpoints from "utilities/endpoints";
import { IOperatingSystemVersion } from "interfaces/operating_system";
-import { OsqueryPlatform } from "interfaces/platform";
+import { Platform } from "interfaces/platform";
import { buildQueryStringFromParams } from "utilities/url";
import { API_NO_TEAM_ID } from "interfaces/team";
@@ -11,10 +11,12 @@ export const OS_VERSIONS_API_SUPPORTED_PLATFORMS = [
"darwin",
"windows",
"chrome",
+ "ios",
+ "ipados",
];
export interface IGetOSVersionsQueryParams {
- platform?: OsqueryPlatform;
+ platform?: Platform | "";
teamId?: number;
os_name?: string;
os_version?: string;
diff --git a/frontend/utilities/constants.tsx b/frontend/utilities/constants.tsx
index f8c484e9b980..b3267c4580a4 100644
--- a/frontend/utilities/constants.tsx
+++ b/frontend/utilities/constants.tsx
@@ -1,6 +1,5 @@
import URL_PREFIX from "router/url_prefix";
-import { OsqueryPlatform } from "interfaces/platform";
-import paths from "router/paths";
+import { DisplayPlatform, Platform } from "interfaces/platform";
import { ISchedulableQuery } from "interfaces/schedulable_query";
import React from "react";
import { IDropdownOption } from "interfaces/dropdownOption";
@@ -200,6 +199,8 @@ const PLATFORM_LABEL_NAMES_FROM_API = [
"Red Hat Linux",
"Ubuntu Linux",
"chrome",
+ "iOS",
+ "iPadOS",
] as const;
type PlatformLabelNameFromAPI = typeof PLATFORM_LABEL_NAMES_FROM_API[number];
@@ -210,7 +211,7 @@ export const isPlatformLabelNameFromAPI = (
return PLATFORM_LABEL_NAMES_FROM_API.includes(s as PlatformLabelNameFromAPI);
};
-export const PLATFORM_DISPLAY_NAMES: Record = {
+export const PLATFORM_DISPLAY_NAMES: Record = {
darwin: "macOS",
macOS: "macOS",
windows: "Windows",
@@ -219,6 +220,8 @@ export const PLATFORM_DISPLAY_NAMES: Record = {
Linux: "Linux",
chrome: "ChromeOS",
ChromeOS: "ChromeOS",
+ ios: "iOS",
+ ipados: "iPadOS",
} as const;
// as returned by the TARGETS API; based on display_text
@@ -234,17 +237,10 @@ export const PLATFORM_LABEL_DISPLAY_NAMES: Record<
"Red Hat Linux": "Red Hat Linux",
"Ubuntu Linux": "Ubuntu Linux",
chrome: "ChromeOS",
+ iOS: "iOS",
+ iPadOS: "iPadOS",
} as const;
-export const PLATFORM_LABEL_DISPLAY_ORDER = [
- "macOS",
- "All Linux",
- "CentOS Linux",
- "Red Hat Linux",
- "Ubuntu Linux",
- "MS Windows",
-] as const;
-
export const PLATFORM_LABEL_DISPLAY_TYPES: Record<
PlatformLabelNameFromAPI,
string
@@ -257,12 +253,14 @@ export const PLATFORM_LABEL_DISPLAY_TYPES: Record<
"Red Hat Linux": "platform",
"Ubuntu Linux": "platform",
chrome: "platform",
+ iOS: "platform",
+ iPadOS: "platform",
} as const;
export const PLATFORM_TYPE_ICONS: Record<
Extract<
PlatformLabelNameFromAPI,
- "All Linux" | "macOS" | "MS Windows" | "chrome"
+ "All Linux" | "macOS" | "MS Windows" | "chrome" | "iOS" | "iPadOS"
>,
IconNames
> = {
@@ -270,47 +268,36 @@ export const PLATFORM_TYPE_ICONS: Record<
macOS: "darwin",
"MS Windows": "windows",
chrome: "chrome",
+ iOS: "iOS",
+ iPadOS: "iPadOS",
} as const;
export const hasPlatformTypeIcon = (
s: string
): s is Extract<
PlatformLabelNameFromAPI,
- "All Linux" | "macOS" | "MS Windows" | "chrome"
+ "All Linux" | "macOS" | "MS Windows" | "chrome" | "iOS" | "iPadOS"
> => {
return !!PLATFORM_TYPE_ICONS[s as keyof typeof PLATFORM_TYPE_ICONS];
};
-interface IPlatformDropdownOptions {
- label: "All" | "Windows" | "Linux" | "macOS" | "ChromeOS";
- value: "all" | "windows" | "linux" | "darwin" | "chrome" | "";
- path?: string;
+export type PlatformLabelOptions = DisplayPlatform | "All";
+
+export type PlatformValueOptions = Platform | "all";
+
+/** Scheduled queries do not support ChromeOS, iOS, or iPadOS */
+interface ISchedulePlatformDropdownOptions {
+ label: Exclude;
+ value: Exclude | "";
}
-export const PLATFORM_DROPDOWN_OPTIONS: IPlatformDropdownOptions[] = [
- { label: "All", value: "all", path: paths.DASHBOARD },
- { label: "macOS", value: "darwin", path: paths.DASHBOARD_MAC },
- { label: "Windows", value: "windows", path: paths.DASHBOARD_WINDOWS },
- { label: "Linux", value: "linux", path: paths.DASHBOARD_LINUX },
- { label: "ChromeOS", value: "chrome", path: paths.DASHBOARD_CHROME },
-];
-// Schedules does not support ChromeOS
-export const SCHEDULE_PLATFORM_DROPDOWN_OPTIONS: IPlatformDropdownOptions[] = [
+export const SCHEDULE_PLATFORM_DROPDOWN_OPTIONS: ISchedulePlatformDropdownOptions[] = [
{ label: "All", value: "" }, // API empty string runs on all platforms
{ label: "macOS", value: "darwin" },
{ label: "Windows", value: "windows" },
{ label: "Linux", value: "linux" },
];
-// Builtin label names returned from API
-export const PLATFORM_NAME_TO_LABEL_NAME = {
- all: "",
- darwin: "macOS",
- windows: "MS Windows",
- linux: "All Linux",
- chrome: "chrome",
-};
-
export const HOSTS_SEARCH_BOX_PLACEHOLDER =
"Search name, hostname, UUID, serial number, or private IP address";
diff --git a/frontend/utilities/sql_tools.ts b/frontend/utilities/sql_tools.ts
index e8255767af90..edf845fc0d79 100644
--- a/frontend/utilities/sql_tools.ts
+++ b/frontend/utilities/sql_tools.ts
@@ -3,11 +3,11 @@ import sqliteParser from "sqlite-parser";
import { intersection, isPlainObject } from "lodash";
import { osqueryTablesAvailable } from "utilities/osquery_tables";
import {
- OsqueryPlatform,
MACADMINS_EXTENSION_TABLES,
SUPPORTED_PLATFORMS,
- SupportedPlatform,
+ QueryablePlatform,
} from "interfaces/platform";
+import { TableSchemaPlatform } from "interfaces/osquery_table";
type IAstNode = Record;
@@ -15,10 +15,10 @@ type IAstNode = Record;
// TODO: Is it ever possible that osquery_tables.json would be missing name or platforms?
interface IOsqueryTable {
name: string;
- platforms: OsqueryPlatform[];
+ platforms: TableSchemaPlatform[];
}
-type IPlatformDictionary = Record;
+type IPlatformDictionary = Record;
const platformsByTableDictionary: IPlatformDictionary = (osqueryTablesAvailable as IOsqueryTable[]).reduce(
(dictionary: IPlatformDictionary, osqueryTable) => {
@@ -57,7 +57,7 @@ const _visit = (
const filterCompatiblePlatforms = (
sqlTables: string[]
-): SupportedPlatform[] => {
+): QueryablePlatform[] => {
if (!sqlTables.length) {
return [...SUPPORTED_PLATFORMS]; // if a query has no tables but is still syntatically valid sql, it is treated as compatible with all platforms
}
@@ -147,7 +147,7 @@ export const checkTable = (
export const checkPlatformCompatibility = (
sqlString: string,
includeCteTables = false
-): { platforms: SupportedPlatform[] | null; error: Error | null } => {
+): { platforms: QueryablePlatform[] | null; error: Error | null } => {
let sqlTables: string[] | undefined;
try {
// get tables from str
diff --git a/frontend/utilities/strings/stringUtils.ts b/frontend/utilities/strings/stringUtils.ts
index e7e205d98eda..6d0c8756feee 100644
--- a/frontend/utilities/strings/stringUtils.ts
+++ b/frontend/utilities/strings/stringUtils.ts
@@ -20,6 +20,8 @@ const capitalizeRole = (str: UserRole): UserRole => {
export const STYLIZATIONS_AND_ACRONYMS = [
"macOS",
+ "iOS",
+ "iPadOS",
"osquery",
"MySQL",
"MDM",
@@ -29,7 +31,7 @@ export const STYLIZATIONS_AND_ACRONYMS = [
];
// fleetdm.com/handbook/marketing/content-style-guide#sentence-case
-// * doesn't recognize proper nouns!
+/** Does not recognize proper nouns! */
export const enforceFleetSentenceCasing = (s: string) => {
const resArr = s.split(" ").map((word, i) => {
if (!STYLIZATIONS_AND_ACRONYMS.includes(word)) {