fleet/frontend/pages/Homepage/Homepage.tsx

432 lines
13 KiB
TypeScript
Raw Normal View History

2022-02-08 05:03:01 +00:00
import React, { useContext, useState } from "react";
import { useQuery } from "react-query";
import { AppContext } from "context/app";
import { find } from "lodash";
import {
IEnrollSecret,
IEnrollSecretsResponse,
} from "interfaces/enroll_secret";
2022-06-10 18:29:45 +00:00
import { IHostSummary, IHostSummaryPlatforms } from "interfaces/host_summary";
import { ILabelSummary } from "interfaces/label";
import { IOsqueryPlatform } from "interfaces/platform";
import { ITeam } from "interfaces/team";
import enrollSecretsAPI from "services/entities/enroll_secret";
2022-06-10 18:29:45 +00:00
import hostSummaryAPI from "services/entities/host_summary";
import teamsAPI, { ILoadTeamsResponse } from "services/entities/teams";
import sortUtils from "utilities/sort";
2022-02-08 05:03:01 +00:00
import { PLATFORM_DROPDOWN_OPTIONS } from "utilities/constants";
import TeamsDropdown from "components/TeamsDropdown";
import Spinner from "components/Spinner";
// @ts-ignore
2022-02-08 05:03:01 +00:00
import Dropdown from "components/forms/fields/Dropdown";
import MainContent from "components/MainContent";
2022-02-08 05:03:01 +00:00
import useInfoCard from "./components/InfoCard";
import HostsStatus from "./cards/HostsStatus";
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";
2022-02-08 05:03:01 +00:00
import MDM from "./cards/MDM";
import Munki from "./cards/Munki";
import OperatingSystems from "./cards/OperatingSystems";
import AddHostsModal from "../../components/AddHostsModal";
2022-02-08 05:03:01 +00:00
import ExternalURLIcon from "../../../assets/images/icon-external-url-12x12@2x.png";
const baseClass = "homepage";
const Homepage = (): JSX.Element => {
const {
config,
currentTeam,
isGlobalAdmin,
isGlobalMaintainer,
isTeamAdmin,
isTeamMaintainer,
isPremiumTier,
isFreeTier,
isSandboxMode,
isOnGlobalTeam,
setCurrentTeam,
} = useContext(AppContext);
2022-02-08 05:03:01 +00:00
const [selectedPlatform, setSelectedPlatform] = useState<string>("");
2022-06-10 18:29:45 +00:00
const [labels, setLabels] = useState<ILabelSummary[]>();
const [macCount, setMacCount] = useState<number>(0);
const [windowsCount, setWindowsCount] = useState<number>(0);
const [linuxCount, setLinuxCount] = useState<number>(0);
const [onlineCount, setOnlineCount] = useState<number>(0);
const [offlineCount, setOfflineCount] = useState<number>(0);
const [showActivityFeedTitle, setShowActivityFeedTitle] = useState<boolean>(
false
);
const [showSoftwareUI, setShowSoftwareUI] = useState<boolean>(false);
2022-02-08 05:03:01 +00:00
const [showMunkiUI, setShowMunkiUI] = useState<boolean>(false);
const [showMDMUI, setShowMDMUI] = useState<boolean>(false);
const [showAddHostsModal, setShowAddHostsModal] = useState<boolean>(false);
const [showOperatingSystemsUI, setShowOperatingSystemsUI] = useState<boolean>(
false
);
const [showHostsUI, setShowHostsUI] = useState<boolean>(false); // Hides UI on first load only
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)),
onSuccess: (responseTeams) => {
if (!currentTeam && !isOnGlobalTeam && responseTeams.length) {
setCurrentTeam(responseTeams[0]);
}
},
});
const {
data: hostSummaryData,
isFetching: isHostSummaryFetching,
error: errorHosts,
} = useQuery<IHostSummary, Error, IHostSummary>(
2022-02-08 05:03:01 +00:00
["host summary", currentTeam, selectedPlatform],
() =>
hostSummaryAPI.getSummary({
teamId: currentTeam?.id,
platform: selectedPlatform,
}),
{
select: (data: IHostSummary) => data,
onSuccess: (data: IHostSummary) => {
setLabels(data.builtin_labels);
setOnlineCount(data.online_count);
setOfflineCount(data.offline_count);
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,
refetch: refetchGlobalSecrets,
} = useQuery<IEnrollSecretsResponse, Error, IEnrollSecret[]>(
["global secrets"],
() => enrollSecretsAPI.getGlobalEnrollSecrets(),
{
enabled: !!canEnrollGlobalHosts,
select: (data: IEnrollSecretsResponse) => data.secrets,
}
);
const {
isLoading: isTeamSecretsLoading,
data: teamSecrets,
refetch: refetchTeamSecrets,
} = useQuery<IEnrollSecretsResponse, Error, IEnrollSecret[]>(
["team secrets", currentTeam],
() => {
if (currentTeam) {
return enrollSecretsAPI.getTeamEnrollSecrets(currentTeam.id);
}
return { secrets: [] };
},
{
enabled: !!currentTeam?.id && !!canEnrollHosts,
select: (data: IEnrollSecretsResponse) => data.secrets,
}
);
2022-02-08 05:03:01 +00:00
const handleTeamSelect = (teamId: number) => {
const selectedTeam = find(teams, ["id", teamId]);
setCurrentTeam(selectedTeam);
};
const toggleAddHostsModal = () => {
setShowAddHostsModal(!showAddHostsModal);
};
2022-02-08 05:03:01 +00:00
const HostsSummaryCard = useInfoCard({
title: "Hosts",
action: {
type: "link",
text: "View all hosts",
},
total_host_count: (() => {
if (!isHostSummaryFetching && !errorHosts) {
return `${hostSummaryData?.totals_hosts_count}` || undefined;
2022-02-08 05:03:01 +00:00
}
return undefined;
})(),
showTitle: true,
children: (
<HostsSummary
currentTeamId={currentTeam?.id}
macCount={macCount}
windowsCount={windowsCount}
linuxCount={linuxCount}
2022-02-08 05:03:01 +00:00
isLoadingHostsSummary={isHostSummaryFetching}
showHostsUI={showHostsUI}
selectedPlatform={selectedPlatform}
labels={labels}
errorHosts={!!errorHosts}
2022-02-08 05:03:01 +00:00
/>
),
});
const HostsStatusCard = useInfoCard({
title: "",
children: (
<HostsStatus
onlineCount={onlineCount}
offlineCount={offlineCount}
isLoadingHosts={isHostSummaryFetching}
showHostsUI={showHostsUI}
/>
),
});
const WelcomeHostCard = useInfoCard({
title: "Welcome to Fleet",
showTitle: true,
children: (
<WelcomeHost
totalsHostsCount={
(hostSummaryData && hostSummaryData.totals_hosts_count) || 0
}
toggleAddHostsModal={toggleAddHostsModal}
/>
),
2022-02-08 05:03:01 +00:00
});
const LearnFleetCard = useInfoCard({
title: "Learn how to use Fleet",
showTitle: true,
2022-02-08 05:03:01 +00:00
children: <LearnFleet />,
});
const ActivityFeedCard = useInfoCard({
title: "Activity",
showTitle: showActivityFeedTitle,
children: (
<ActivityFeed setShowActivityFeedTitle={setShowActivityFeedTitle} />
),
});
const SoftwareCard = useInfoCard({
title: "Software",
action: {
type: "link",
text: "View all software",
to: "software",
},
showTitle: showSoftwareUI,
children: (
<Software
currentTeamId={currentTeam?.id}
setShowSoftwareUI={setShowSoftwareUI}
showSoftwareUI={showSoftwareUI}
/>
),
});
const MunkiCard = useInfoCard({
title: "Munki versions",
showTitle: showMunkiUI,
description: (
<p>
Munki is a tool for managing software on macOS devices.{" "}
<a
target="_blank"
rel="noreferrer noopener"
href="https://www.munki.org/munki/"
>
Learn about Munki <img src={ExternalURLIcon} alt="" />
</a>
</p>
),
children: (
<Munki
setShowMunkiUI={setShowMunkiUI}
showMunkiUI={showMunkiUI}
currentTeamId={currentTeam?.id}
/>
),
});
const MDMCard = useInfoCard({
title: "Mobile device management (MDM) enrollment",
showTitle: showMDMUI,
description: (
<p>
MDM is used to manage configuration on macOS devices.{" "}
<a
target="_blank"
rel="noreferrer noopener"
href="https://support.apple.com/guide/deployment/intro-to-mdm-depc0aadd3fe/web"
>
Learn about MDM <img src={ExternalURLIcon} alt="" />
</a>
</p>
),
children: (
<MDM
setShowMDMUI={setShowMDMUI}
showMDMUI={showMDMUI}
currentTeamId={currentTeam?.id}
/>
),
});
const OperatingSystemsCard = useInfoCard({
title: "Operating systems",
showTitle: showOperatingSystemsUI,
children: (
<OperatingSystems
currentTeamId={currentTeam?.id}
selectedPlatform={selectedPlatform as IOsqueryPlatform}
showTitle={showOperatingSystemsUI}
setShowTitle={setShowOperatingSystemsUI}
/>
),
});
const allLayout = () => {
return (
<div className={`${baseClass}__section`}>
{!currentTeam &&
canEnrollGlobalHosts &&
hostSummaryData &&
hostSummaryData?.totals_hosts_count < 2 && (
<>
{WelcomeHostCard}
{LearnFleetCard}
</>
)}
{SoftwareCard}
{!currentTeam && isOnGlobalTeam && <>{ActivityFeedCard}</>}
</div>
);
};
2022-02-08 05:03:01 +00:00
const macOSLayout = () => (
<div className={`${baseClass}__section`}>
{OperatingSystemsCard}
2022-02-08 05:03:01 +00:00
{MunkiCard}
{MDMCard}
</div>
);
const windowsLayout = () => (
<div className={`${baseClass}__section`}>{OperatingSystemsCard}</div>
);
2022-02-08 05:03:01 +00:00
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.
currentTeam && !isSandboxMode
? teamSecrets?.[0].secret
: globalSecrets?.[0].secret;
return (
<AddHostsModal
currentTeam={currentTeam}
enrollSecret={enrollSecret}
isLoading={isLoadingTeams || isGlobalSecretsLoading}
isSandboxMode={!!isSandboxMode}
onCancel={toggleAddHostsModal}
/>
);
};
return (
<MainContent className={baseClass}>
<div className={`${baseClass}__wrapper`}>
2022-02-08 05:03:01 +00:00
<div className={`${baseClass}__header`}>
<div className={`${baseClass}__text`}>
<div className={`${baseClass}__title`}>
{isFreeTier && <h1>{config?.org_info.org_name}</h1>}
2022-02-08 05:03:01 +00:00
{isPremiumTier &&
teams &&
(teams.length > 1 || isOnGlobalTeam) && (
<TeamsDropdown
selectedTeamId={currentTeam?.id || 0}
currentUserTeams={teams || []}
onChange={(newSelectedValue: number) =>
handleTeamSelect(newSelectedValue)
}
/>
)}
{isPremiumTier &&
!isOnGlobalTeam &&
teams &&
teams.length === 1 && <h1>{teams[0].name}</h1>}
</div>
</div>
</div>
2022-02-08 05:03:01 +00:00
<div className={`${baseClass}__platforms`}>
<span>Platform:&nbsp;</span>
<Dropdown
value={selectedPlatform}
className={`${baseClass}__platform_dropdown`}
options={PLATFORM_DROPDOWN_OPTIONS}
searchable={false}
onChange={(value: string) => setSelectedPlatform(value)}
/>
</div>
<div className="host-sections">
<>
2022-02-08 05:03:01 +00:00
{isHostSummaryFetching && (
<div className="spinner">
<Spinner />
</div>
)}
2022-02-08 05:03:01 +00:00
<div className={`${baseClass}__section`}>{HostsSummaryCard}</div>
<div className={`${baseClass}__section`}>{HostsStatusCard}</div>
</>
</div>
2022-02-08 05:03:01 +00:00
{renderCards()}
{showAddHostsModal && renderAddHostsModal()}
</div>
</MainContent>
);
};
export default Homepage;