mirror of
https://github.com/empayre/fleet.git
synced 2024-11-07 01:15:22 +00:00
774 lines
23 KiB
TypeScript
774 lines
23 KiB
TypeScript
import React, { useContext, useState, useEffect } from "react";
|
|
import { InjectedRouter } from "react-router";
|
|
import { useQuery } from "react-query";
|
|
|
|
import { AppContext } from "context/app";
|
|
import paths from "router/paths";
|
|
|
|
import {
|
|
IEnrollSecret,
|
|
IEnrollSecretsResponse,
|
|
} from "interfaces/enroll_secret";
|
|
import { IHostSummary, IHostSummaryPlatforms } from "interfaces/host_summary";
|
|
import { ILabelSummary } from "interfaces/label";
|
|
import {
|
|
IMacadminAggregate,
|
|
IMunkiIssuesAggregate,
|
|
IMunkiVersionsAggregate,
|
|
} from "interfaces/macadmins";
|
|
import {
|
|
IMdmStatusCardData,
|
|
IMdmSolution,
|
|
IMdmSummaryResponse,
|
|
} from "interfaces/mdm";
|
|
import { ISelectedPlatform } from "interfaces/platform";
|
|
import { ISoftwareResponse, ISoftwareCountResponse } from "interfaces/software";
|
|
import { ITeam } from "interfaces/team";
|
|
import { useTeamIdParam } from "hooks/useTeamIdParam";
|
|
import enrollSecretsAPI from "services/entities/enroll_secret";
|
|
import hostSummaryAPI from "services/entities/host_summary";
|
|
import macadminsAPI from "services/entities/macadmins";
|
|
import softwareAPI, {
|
|
ISoftwareQueryKey,
|
|
ISoftwareCountQueryKey,
|
|
} from "services/entities/software";
|
|
import teamsAPI, { ILoadTeamsResponse } from "services/entities/teams";
|
|
import hosts from "services/entities/hosts";
|
|
import sortUtils from "utilities/sort";
|
|
import {
|
|
PLATFORM_DROPDOWN_OPTIONS,
|
|
PLATFORM_NAME_TO_LABEL_NAME,
|
|
} from "utilities/constants";
|
|
import { ITableQueryData } from "components/TableContainer/TableContainer";
|
|
|
|
import TeamsDropdown from "components/TeamsDropdown";
|
|
import Spinner from "components/Spinner";
|
|
import CustomLink from "components/CustomLink";
|
|
// @ts-ignore
|
|
import Dropdown from "components/forms/fields/Dropdown";
|
|
import MainContent from "components/MainContent";
|
|
import LastUpdatedText from "components/LastUpdatedText";
|
|
import SandboxGate from "components/Sandbox/SandboxGate";
|
|
import useInfoCard from "./components/InfoCard";
|
|
import MissingHosts from "./cards/MissingHosts";
|
|
import LowDiskSpaceHosts from "./cards/LowDiskSpaceHosts";
|
|
import HostsSummary from "./cards/HostsSummary";
|
|
import ActivityFeed from "./cards/ActivityFeed";
|
|
import Software from "./cards/Software";
|
|
import LearnFleet from "./cards/LearnFleet";
|
|
import WelcomeHost from "./cards/WelcomeHost";
|
|
import Mdm from "./cards/MDM";
|
|
import Munki from "./cards/Munki";
|
|
import OperatingSystems from "./cards/OperatingSystems";
|
|
import AddHostsModal from "../../components/AddHostsModal";
|
|
|
|
const baseClass = "dashboard-page";
|
|
|
|
// Premium feature, Gb must be set between 1-100
|
|
const LOW_DISK_SPACE_GB = 32;
|
|
|
|
interface IDashboardProps {
|
|
router: InjectedRouter; // v3
|
|
location: {
|
|
pathname: string;
|
|
search: string;
|
|
hash?: string;
|
|
query: {
|
|
team_id?: string;
|
|
};
|
|
};
|
|
}
|
|
|
|
const DashboardPage = ({ router, location }: IDashboardProps): JSX.Element => {
|
|
const { pathname } = location;
|
|
const {
|
|
config,
|
|
isGlobalAdmin,
|
|
isGlobalMaintainer,
|
|
isPremiumTier,
|
|
isSandboxMode,
|
|
isOnGlobalTeam,
|
|
} = useContext(AppContext);
|
|
|
|
const {
|
|
currentTeamId,
|
|
currentTeamName,
|
|
isAnyTeamSelected,
|
|
isRouteOk,
|
|
isTeamAdmin,
|
|
isTeamMaintainer,
|
|
teamIdForApi,
|
|
userTeams,
|
|
handleTeamChange,
|
|
} = useTeamIdParam({
|
|
location,
|
|
router,
|
|
includeAllTeams: true,
|
|
includeNoTeam: false,
|
|
});
|
|
|
|
const [selectedPlatform, setSelectedPlatform] = useState<ISelectedPlatform>(
|
|
"all"
|
|
);
|
|
const [
|
|
selectedPlatformLabelId,
|
|
setSelectedPlatformLabelId,
|
|
] = useState<number>();
|
|
const [labels, setLabels] = useState<ILabelSummary[]>();
|
|
const [macCount, setMacCount] = useState(0);
|
|
const [windowsCount, setWindowsCount] = useState(0);
|
|
const [linuxCount, setLinuxCount] = useState(0);
|
|
const [missingCount, setMissingCount] = useState(0);
|
|
const [lowDiskSpaceCount, setLowDiskSpaceCount] = useState(0);
|
|
const [showActivityFeedTitle, setShowActivityFeedTitle] = useState(false);
|
|
const [softwareTitleDetail, setSoftwareTitleDetail] = useState<
|
|
JSX.Element | string | null
|
|
>("");
|
|
const [softwareNavTabIndex, setSoftwareNavTabIndex] = useState(0);
|
|
const [softwarePageIndex, setSoftwarePageIndex] = useState(0);
|
|
const [softwareActionUrl, setSoftwareActionUrl] = useState<string>();
|
|
const [showMunkiCard, setShowMunkiCard] = useState(true);
|
|
const [showMdmCard, setShowMdmCard] = useState(true);
|
|
const [showSoftwareCard, setShowSoftwareCard] = useState(false);
|
|
const [showAddHostsModal, setShowAddHostsModal] = useState(false);
|
|
const [showOperatingSystemsUI, setShowOperatingSystemsUI] = useState(false);
|
|
const [showHostsUI, setShowHostsUI] = useState(false); // Hides UI on first load only
|
|
const [mdmStatusData, setMdmStatusData] = useState<IMdmStatusCardData[]>([]);
|
|
const [mdmSolutions, setMdmSolutions] = useState<IMdmSolution[] | null>([]);
|
|
|
|
const [munkiIssuesData, setMunkiIssuesData] = useState<
|
|
IMunkiIssuesAggregate[]
|
|
>([]);
|
|
const [munkiVersionsData, setMunkiVersionsData] = useState<
|
|
IMunkiVersionsAggregate[]
|
|
>([]);
|
|
const [mdmTitleDetail, setMdmTitleDetail] = useState<
|
|
JSX.Element | string | null
|
|
>();
|
|
const [munkiTitleDetail, setMunkiTitleDetail] = useState<
|
|
JSX.Element | string | null
|
|
>();
|
|
|
|
useEffect(() => {
|
|
const platformByPathname =
|
|
PLATFORM_DROPDOWN_OPTIONS?.find((platform) => platform.path === pathname)
|
|
?.value || "all";
|
|
|
|
setSelectedPlatform(platformByPathname);
|
|
}, [pathname]);
|
|
|
|
const canEnrollHosts =
|
|
isGlobalAdmin || isGlobalMaintainer || isTeamAdmin || isTeamMaintainer;
|
|
const canEnrollGlobalHosts = isGlobalAdmin || isGlobalMaintainer;
|
|
|
|
const { data: teams, isLoading: isLoadingTeams } = useQuery<
|
|
ILoadTeamsResponse,
|
|
Error,
|
|
ITeam[]
|
|
>(["teams"], () => teamsAPI.loadAll(), {
|
|
enabled: !!isPremiumTier,
|
|
select: (data: ILoadTeamsResponse) =>
|
|
data.teams.sort((a, b) => sortUtils.caseInsensitiveAsc(a.name, b.name)),
|
|
});
|
|
|
|
const {
|
|
data: hostSummaryData,
|
|
isFetching: isHostSummaryFetching,
|
|
error: errorHosts,
|
|
} = useQuery<IHostSummary, Error, IHostSummary>(
|
|
["host summary", teamIdForApi, isPremiumTier, selectedPlatform],
|
|
() =>
|
|
hostSummaryAPI.getSummary({
|
|
teamId: teamIdForApi,
|
|
platform: selectedPlatform !== "all" ? selectedPlatform : undefined,
|
|
lowDiskSpace: isPremiumTier ? LOW_DISK_SPACE_GB : undefined,
|
|
}),
|
|
{
|
|
enabled: isRouteOk,
|
|
select: (data: IHostSummary) => data,
|
|
onSuccess: (data: IHostSummary) => {
|
|
setLabels(data.builtin_labels);
|
|
if (isPremiumTier) {
|
|
setMissingCount(data.missing_30_days_count || 0);
|
|
setLowDiskSpaceCount(data.low_disk_space_count || 0);
|
|
}
|
|
const macHosts = data.platforms?.find(
|
|
(platform: IHostSummaryPlatforms) => platform.platform === "darwin"
|
|
) || { platform: "darwin", hosts_count: 0 };
|
|
|
|
const windowsHosts = data.platforms?.find(
|
|
(platform: IHostSummaryPlatforms) => platform.platform === "windows"
|
|
) || { platform: "windows", hosts_count: 0 };
|
|
|
|
setMacCount(macHosts.hosts_count);
|
|
setWindowsCount(windowsHosts.hosts_count);
|
|
setLinuxCount(data.all_linux_count);
|
|
setShowHostsUI(true);
|
|
},
|
|
}
|
|
);
|
|
|
|
const { isLoading: isGlobalSecretsLoading, data: globalSecrets } = useQuery<
|
|
IEnrollSecretsResponse,
|
|
Error,
|
|
IEnrollSecret[]
|
|
>(["global secrets"], () => enrollSecretsAPI.getGlobalEnrollSecrets(), {
|
|
enabled: isRouteOk && canEnrollGlobalHosts,
|
|
select: (data: IEnrollSecretsResponse) => data.secrets,
|
|
});
|
|
|
|
const { data: teamSecrets } = useQuery<
|
|
IEnrollSecretsResponse,
|
|
Error,
|
|
IEnrollSecret[]
|
|
>(
|
|
["team secrets", teamIdForApi],
|
|
() => {
|
|
if (isAnyTeamSelected) {
|
|
return enrollSecretsAPI.getTeamEnrollSecrets(teamIdForApi);
|
|
}
|
|
return { secrets: [] };
|
|
},
|
|
{
|
|
enabled: isRouteOk && isAnyTeamSelected && canEnrollHosts,
|
|
select: (data: IEnrollSecretsResponse) => data.secrets,
|
|
}
|
|
);
|
|
|
|
const featuresConfig = isAnyTeamSelected
|
|
? teams?.find((t) => t.id === currentTeamId)?.features
|
|
: config?.features;
|
|
const isSoftwareEnabled = !!featuresConfig?.enable_software_inventory;
|
|
|
|
const SOFTWARE_DEFAULT_SORT_DIRECTION = "desc";
|
|
const SOFTWARE_DEFAULT_SORT_HEADER = "hosts_count";
|
|
const SOFTWARE_DEFAULT_PAGE_SIZE = 8;
|
|
|
|
const {
|
|
data: software,
|
|
isFetching: isSoftwareFetching,
|
|
error: errorSoftware,
|
|
} = useQuery<
|
|
ISoftwareResponse,
|
|
Error,
|
|
ISoftwareResponse,
|
|
ISoftwareQueryKey[]
|
|
>(
|
|
[
|
|
{
|
|
scope: "software",
|
|
page: softwarePageIndex,
|
|
perPage: SOFTWARE_DEFAULT_PAGE_SIZE,
|
|
orderDirection: SOFTWARE_DEFAULT_SORT_DIRECTION,
|
|
orderKey: SOFTWARE_DEFAULT_SORT_HEADER,
|
|
teamId: teamIdForApi,
|
|
vulnerable: !!softwareNavTabIndex, // we can take the tab index as a boolean to represent the vulnerable flag :)
|
|
},
|
|
],
|
|
({ queryKey }) => softwareAPI.load(queryKey[0]),
|
|
{
|
|
enabled: isRouteOk && isSoftwareEnabled,
|
|
keepPreviousData: true,
|
|
staleTime: 30000, // stale time can be adjusted if fresher data is desired based on software inventory interval
|
|
onSuccess: (data) => {
|
|
if (data.software?.length > 0) {
|
|
setSoftwareTitleDetail &&
|
|
setSoftwareTitleDetail(
|
|
<LastUpdatedText
|
|
lastUpdatedAt={data.counts_updated_at}
|
|
whatToRetrieve={"software"}
|
|
/>
|
|
);
|
|
setShowSoftwareCard(true);
|
|
} else {
|
|
setShowSoftwareCard(false);
|
|
}
|
|
},
|
|
}
|
|
);
|
|
|
|
// If no vulnerable software, !software?.software can return undefined
|
|
// Must check non-vuln software count > 0 to show software card iff API returning undefined
|
|
const { data: softwareCount } = useQuery<
|
|
ISoftwareCountResponse,
|
|
Error,
|
|
number,
|
|
ISoftwareCountQueryKey[]
|
|
>(
|
|
[
|
|
{
|
|
scope: "softwareCount",
|
|
teamId: teamIdForApi,
|
|
},
|
|
],
|
|
({ queryKey }) => softwareAPI.count(queryKey[0]),
|
|
{
|
|
enabled: isRouteOk && !software?.software,
|
|
keepPreviousData: true,
|
|
refetchOnWindowFocus: false,
|
|
retry: 1,
|
|
select: (data) => data.count,
|
|
}
|
|
);
|
|
|
|
const { isFetching: isMdmFetching, error: errorMdm } = useQuery<
|
|
IMdmSummaryResponse,
|
|
Error
|
|
>(
|
|
[`mdm-${selectedPlatform}`, teamIdForApi],
|
|
() => hosts.getMdmSummary(selectedPlatform, teamIdForApi),
|
|
{
|
|
enabled: isRouteOk && selectedPlatform !== "linux",
|
|
onSuccess: ({
|
|
counts_updated_at,
|
|
mobile_device_management_solution,
|
|
mobile_device_management_enrollment_status: {
|
|
enrolled_automated_hosts_count,
|
|
enrolled_manual_hosts_count,
|
|
unenrolled_hosts_count,
|
|
pending_hosts_count,
|
|
hosts_count,
|
|
},
|
|
}) => {
|
|
if (hosts_count === 0 && mobile_device_management_solution === null) {
|
|
setShowMdmCard(false);
|
|
return;
|
|
}
|
|
|
|
setMdmTitleDetail(
|
|
<LastUpdatedText
|
|
lastUpdatedAt={counts_updated_at}
|
|
whatToRetrieve={"MDM information"}
|
|
/>
|
|
);
|
|
const statusData: IMdmStatusCardData[] = [
|
|
{
|
|
status: "On (manual)",
|
|
hosts: enrolled_manual_hosts_count,
|
|
},
|
|
{
|
|
status: "On (automatic)",
|
|
hosts: enrolled_automated_hosts_count,
|
|
},
|
|
{ status: "Off", hosts: unenrolled_hosts_count },
|
|
];
|
|
isPremiumTier &&
|
|
statusData.push({
|
|
status: "Pending",
|
|
hosts: pending_hosts_count || 0,
|
|
});
|
|
setMdmStatusData(statusData);
|
|
setMdmSolutions(mobile_device_management_solution);
|
|
setShowMdmCard(true);
|
|
},
|
|
}
|
|
);
|
|
|
|
const { isFetching: isMacAdminsFetching, error: errorMacAdmins } = useQuery<
|
|
IMacadminAggregate,
|
|
Error
|
|
>(["macAdmins", teamIdForApi], () => macadminsAPI.loadAll(teamIdForApi), {
|
|
keepPreviousData: true,
|
|
enabled: isRouteOk && selectedPlatform === "darwin",
|
|
onSuccess: ({
|
|
macadmins: { munki_issues, munki_versions, counts_updated_at },
|
|
}) => {
|
|
setMunkiVersionsData(munki_versions);
|
|
setMunkiIssuesData(munki_issues);
|
|
setShowMunkiCard(!!munki_versions);
|
|
setMunkiTitleDetail(
|
|
<LastUpdatedText
|
|
lastUpdatedAt={counts_updated_at}
|
|
whatToRetrieve={"Munki"}
|
|
/>
|
|
);
|
|
},
|
|
});
|
|
|
|
useEffect(() => {
|
|
softwareCount && softwareCount > 0
|
|
? setShowSoftwareCard(true)
|
|
: setShowSoftwareCard(false);
|
|
}, [softwareCount]);
|
|
|
|
// Sets selected platform label id for links to filtered manage host page
|
|
useEffect(() => {
|
|
if (labels) {
|
|
const getLabel = (
|
|
labelString: string,
|
|
summaryLabels: ILabelSummary[]
|
|
): ILabelSummary | undefined => {
|
|
return Object.values(summaryLabels).find((label: ILabelSummary) => {
|
|
return label.label_type === "builtin" && label.name === labelString;
|
|
});
|
|
};
|
|
|
|
if (selectedPlatform !== "all") {
|
|
const labelValue = PLATFORM_NAME_TO_LABEL_NAME[selectedPlatform];
|
|
setSelectedPlatformLabelId(getLabel(labelValue, labels)?.id);
|
|
} else {
|
|
setSelectedPlatformLabelId(undefined);
|
|
}
|
|
}
|
|
}, [labels, selectedPlatform]);
|
|
|
|
const toggleAddHostsModal = () => {
|
|
setShowAddHostsModal(!showAddHostsModal);
|
|
};
|
|
|
|
const HostsSummaryCard = useInfoCard({
|
|
title: "Hosts",
|
|
action: {
|
|
type: "link",
|
|
text: "View all hosts",
|
|
},
|
|
total_host_count: (() => {
|
|
if (!isHostSummaryFetching && !errorHosts) {
|
|
return `${hostSummaryData?.totals_hosts_count}` || undefined;
|
|
}
|
|
|
|
return undefined;
|
|
})(),
|
|
showTitle: true,
|
|
children: (
|
|
<HostsSummary
|
|
currentTeamId={teamIdForApi}
|
|
macCount={macCount}
|
|
windowsCount={windowsCount}
|
|
linuxCount={linuxCount}
|
|
isLoadingHostsSummary={isHostSummaryFetching}
|
|
showHostsUI={showHostsUI}
|
|
selectedPlatform={selectedPlatform}
|
|
errorHosts={!!errorHosts}
|
|
/>
|
|
),
|
|
});
|
|
|
|
// NOTE: this is called once on the initial rendering. The initial render of
|
|
// the TableContainer child component will call this handler.
|
|
const onSoftwareQueryChange = async ({
|
|
pageIndex: newPageIndex,
|
|
}: ITableQueryData) => {
|
|
if (softwarePageIndex !== newPageIndex) {
|
|
setSoftwarePageIndex(newPageIndex);
|
|
}
|
|
};
|
|
|
|
const onSoftwareTabChange = (index: number) => {
|
|
const { MANAGE_SOFTWARE } = paths;
|
|
setSoftwareNavTabIndex(index);
|
|
setSoftwareActionUrl &&
|
|
setSoftwareActionUrl(
|
|
index === 1 ? `${MANAGE_SOFTWARE}?vulnerable=true` : MANAGE_SOFTWARE
|
|
);
|
|
};
|
|
|
|
// TODO: Rework after backend is adjusted to differentiate empty search/filter results from
|
|
// collecting inventory
|
|
const isCollectingInventory =
|
|
!isAnyTeamSelected &&
|
|
!softwarePageIndex &&
|
|
!software?.software &&
|
|
software?.counts_updated_at === null;
|
|
|
|
const MissingHostsCard = useInfoCard({
|
|
title: "",
|
|
children: (
|
|
<MissingHosts
|
|
missingCount={missingCount}
|
|
isLoadingHosts={isHostSummaryFetching}
|
|
showHostsUI={showHostsUI}
|
|
selectedPlatformLabelId={selectedPlatformLabelId}
|
|
currentTeamId={teamIdForApi}
|
|
isSandboxMode={isSandboxMode}
|
|
/>
|
|
),
|
|
});
|
|
|
|
const LowDiskSpaceHostsCard = useInfoCard({
|
|
title: "",
|
|
children: (
|
|
<LowDiskSpaceHosts
|
|
lowDiskSpaceGb={LOW_DISK_SPACE_GB}
|
|
lowDiskSpaceCount={lowDiskSpaceCount}
|
|
isLoadingHosts={isHostSummaryFetching}
|
|
showHostsUI={showHostsUI}
|
|
selectedPlatformLabelId={selectedPlatformLabelId}
|
|
currentTeamId={teamIdForApi}
|
|
isSandboxMode={isSandboxMode}
|
|
/>
|
|
),
|
|
});
|
|
|
|
const WelcomeHostCard = useInfoCard({
|
|
title: "Welcome to Fleet",
|
|
showTitle: true,
|
|
children: (
|
|
<WelcomeHost
|
|
totalsHostsCount={
|
|
(hostSummaryData && hostSummaryData.totals_hosts_count) || 0
|
|
}
|
|
toggleAddHostsModal={toggleAddHostsModal}
|
|
/>
|
|
),
|
|
});
|
|
|
|
const LearnFleetCard = useInfoCard({
|
|
title: "Learn how to use Fleet",
|
|
showTitle: true,
|
|
children: <LearnFleet />,
|
|
});
|
|
|
|
const ActivityFeedCard = useInfoCard({
|
|
title: "Activity",
|
|
showTitle: showActivityFeedTitle,
|
|
children: (
|
|
<ActivityFeed
|
|
setShowActivityFeedTitle={setShowActivityFeedTitle}
|
|
isPremiumTier={isPremiumTier || false}
|
|
isSandboxMode={isSandboxMode}
|
|
/>
|
|
),
|
|
});
|
|
|
|
const SoftwareCard = useInfoCard({
|
|
title: "Software",
|
|
action: {
|
|
type: "link",
|
|
text: "View all software",
|
|
to: "software",
|
|
},
|
|
actionUrl: softwareActionUrl,
|
|
titleDetail: softwareTitleDetail,
|
|
showTitle: !isSoftwareFetching,
|
|
children: (
|
|
<Software
|
|
errorSoftware={errorSoftware}
|
|
isCollectingInventory={isCollectingInventory}
|
|
isSoftwareFetching={isSoftwareFetching}
|
|
isSoftwareEnabled={isSoftwareEnabled}
|
|
software={software}
|
|
teamId={currentTeamId}
|
|
pageIndex={softwarePageIndex}
|
|
navTabIndex={softwareNavTabIndex}
|
|
onTabChange={onSoftwareTabChange}
|
|
onQueryChange={onSoftwareQueryChange}
|
|
router={router}
|
|
/>
|
|
),
|
|
});
|
|
|
|
const MunkiCard = useInfoCard({
|
|
title: "Munki",
|
|
titleDetail: munkiTitleDetail,
|
|
showTitle: !isMacAdminsFetching,
|
|
description: (
|
|
<p>
|
|
Munki is a tool for managing software on macOS devices.{" "}
|
|
<CustomLink
|
|
url="https://www.munki.org/munki/"
|
|
text="Learn about Munki"
|
|
newTab
|
|
/>
|
|
</p>
|
|
),
|
|
children: (
|
|
<Munki
|
|
errorMacAdmins={errorMacAdmins}
|
|
isMacAdminsFetching={isMacAdminsFetching}
|
|
munkiIssuesData={munkiIssuesData}
|
|
munkiVersionsData={munkiVersionsData}
|
|
selectedTeamId={currentTeamId}
|
|
/>
|
|
),
|
|
});
|
|
|
|
const MDMCard = (
|
|
<SandboxGate>
|
|
{useInfoCard({
|
|
title: "Mobile device management (MDM)",
|
|
titleDetail: mdmTitleDetail,
|
|
showTitle: !isMacAdminsFetching,
|
|
description: (
|
|
<p>
|
|
MDM is used to change settings and install software on your hosts.
|
|
</p>
|
|
),
|
|
children: (
|
|
<Mdm
|
|
isFetching={isMdmFetching}
|
|
error={errorMdm}
|
|
mdmStatusData={mdmStatusData}
|
|
mdmSolutions={mdmSolutions}
|
|
selectedPlatformLabelId={selectedPlatformLabelId}
|
|
selectedTeamId={currentTeamId}
|
|
/>
|
|
),
|
|
})}
|
|
</SandboxGate>
|
|
);
|
|
|
|
const OperatingSystemsCard = useInfoCard({
|
|
title: "Operating systems",
|
|
showTitle: showOperatingSystemsUI,
|
|
children: (
|
|
<OperatingSystems
|
|
currentTeamId={teamIdForApi}
|
|
selectedPlatform={selectedPlatform}
|
|
showTitle={showOperatingSystemsUI}
|
|
setShowTitle={setShowOperatingSystemsUI}
|
|
/>
|
|
),
|
|
});
|
|
|
|
const allLayout = () => {
|
|
return (
|
|
<div className={`${baseClass}__section`}>
|
|
{!isAnyTeamSelected &&
|
|
canEnrollGlobalHosts &&
|
|
hostSummaryData &&
|
|
hostSummaryData?.totals_hosts_count < 2 && (
|
|
<>
|
|
{WelcomeHostCard}
|
|
{LearnFleetCard}
|
|
</>
|
|
)}
|
|
{showSoftwareCard && SoftwareCard}
|
|
{!isAnyTeamSelected && isOnGlobalTeam && <>{ActivityFeedCard}</>}
|
|
{showMdmCard && <>{MDMCard}</>}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const macOSLayout = () => (
|
|
<>
|
|
<div className={`${baseClass}__section`}>{OperatingSystemsCard}</div>
|
|
{showMdmCard && <div className={`${baseClass}__section`}>{MDMCard}</div>}
|
|
{showMunkiCard && (
|
|
<div className={`${baseClass}__section`}>{MunkiCard}</div>
|
|
)}
|
|
</>
|
|
);
|
|
|
|
const windowsLayout = () => (
|
|
<>
|
|
<div className={`${baseClass}__section`}>{OperatingSystemsCard}</div>
|
|
{showMdmCard && <div className={`${baseClass}__section`}>{MDMCard}</div>}
|
|
</>
|
|
);
|
|
const linuxLayout = () => null;
|
|
|
|
const renderCards = () => {
|
|
switch (selectedPlatform) {
|
|
case "darwin":
|
|
return macOSLayout();
|
|
case "windows":
|
|
return windowsLayout();
|
|
case "linux":
|
|
return linuxLayout();
|
|
default:
|
|
return allLayout();
|
|
}
|
|
};
|
|
|
|
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;
|
|
|
|
return (
|
|
<AddHostsModal
|
|
currentTeamName={currentTeamName}
|
|
enrollSecret={enrollSecret}
|
|
isAnyTeamSelected={isAnyTeamSelected}
|
|
isLoading={isLoadingTeams || isGlobalSecretsLoading}
|
|
isSandboxMode={!!isSandboxMode}
|
|
onCancel={toggleAddHostsModal}
|
|
/>
|
|
);
|
|
};
|
|
|
|
const renderDashboardHeader = () => {
|
|
if (isPremiumTier) {
|
|
if (userTeams) {
|
|
if (userTeams.length > 1 || isOnGlobalTeam) {
|
|
return (
|
|
<TeamsDropdown
|
|
selectedTeamId={currentTeamId}
|
|
currentUserTeams={userTeams}
|
|
onChange={handleTeamChange}
|
|
isSandboxMode={isSandboxMode}
|
|
/>
|
|
);
|
|
}
|
|
if (userTeams.length === 1) {
|
|
return <h1>{userTeams[0].name}</h1>;
|
|
}
|
|
}
|
|
// userTeams.length should have at least 1 element
|
|
return null;
|
|
}
|
|
// Free tier
|
|
return <h1>{config?.org_info.org_name}</h1>;
|
|
};
|
|
return !isRouteOk ? (
|
|
<Spinner />
|
|
) : (
|
|
<MainContent className={baseClass}>
|
|
<div className={`${baseClass}__wrapper`}>
|
|
<div className={`${baseClass}__header`}>
|
|
<div className={`${baseClass}__text`}>
|
|
<div className={`${baseClass}__title`}>
|
|
{renderDashboardHeader()}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className={`${baseClass}__platforms`}>
|
|
<span>Platform: </span>
|
|
<Dropdown
|
|
value={selectedPlatform || ""}
|
|
className={`${baseClass}__platform_dropdown`}
|
|
options={PLATFORM_DROPDOWN_OPTIONS}
|
|
searchable={false}
|
|
onChange={(value: ISelectedPlatform) => {
|
|
const selectedPlatformOption = PLATFORM_DROPDOWN_OPTIONS.find(
|
|
(platform) => platform.value === value
|
|
);
|
|
router.push(
|
|
(selectedPlatformOption?.path || paths.DASHBOARD)
|
|
.concat(location.search)
|
|
.concat(location.hash || "")
|
|
);
|
|
}}
|
|
/>
|
|
</div>
|
|
<div className="host-sections">
|
|
<>
|
|
{isHostSummaryFetching && (
|
|
<div className="spinner">
|
|
<Spinner />
|
|
</div>
|
|
)}
|
|
<div className={`${baseClass}__section`}>{HostsSummaryCard}</div>
|
|
{isPremiumTier && (
|
|
<div className={`${baseClass}__section`}>
|
|
{MissingHostsCard}
|
|
{LowDiskSpaceHostsCard}
|
|
</div>
|
|
)}
|
|
</>
|
|
</div>
|
|
{renderCards()}
|
|
{showAddHostsModal && renderAddHostsModal()}
|
|
</div>
|
|
</MainContent>
|
|
);
|
|
};
|
|
|
|
export default DashboardPage;
|