fleet/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareAppStoreVpp/SoftwareAppStoreVpp.tsx

251 lines
6.9 KiB
TypeScript

import React, { useContext, useState } from "react";
import { InjectedRouter } from "react-router";
import { useQuery } from "react-query";
import { AxiosError } from "axios";
import PATHS from "router/paths";
import { NotificationContext } from "context/notification";
import { AppContext } from "context/app";
import { ILabelSummary } from "interfaces/label";
import mdmAppleAPI, {
IGetVppTokensResponse,
} from "services/entities/mdm_apple";
import labelsAPI, { getCustomLabels } from "services/entities/labels";
import {
DEFAULT_USE_QUERY_OPTIONS,
LEARN_MORE_ABOUT_BASE_LINK,
} from "utilities/constants";
import CustomLink from "components/CustomLink";
import DataError from "components/DataError";
import Spinner from "components/Spinner";
import PremiumFeatureMessage from "components/PremiumFeatureMessage";
import Button from "components/buttons/Button";
import CategoriesEndUserExperienceModal from "pages/SoftwarePage/components/modals/CategoriesEndUserExperienceModal";
import { getPathWithQueryParams } from "utilities/url";
import SoftwareVppForm from "../../components/forms/SoftwareVppForm";
import { getErrorMessage, teamHasVPPToken } from "./helpers";
import { ISoftwareVppFormData } from "../../components/forms/SoftwareVppForm/SoftwareVppForm";
const baseClass = "software-app-store-vpp";
//
interface IEnableVppMessage {
onEnableVpp: () => void;
}
const EnableVppMessage = ({ onEnableVpp }: IEnableVppMessage) => (
<div className={`${baseClass}__enable-vpp-message`}>
<p className={`${baseClass}__enable-vpp-title`}>
Volume Purchasing Program (VPP) isn&apos;t enabled
</p>
<p className={`${baseClass}__enable-vpp-description`}>
To add App Store apps, first enable VPP.
</p>
<Button onClick={onEnableVpp}>Enable VPP</Button>
</div>
);
interface IAddTeamToVppMessage {
onEditVpp: () => void;
}
const AddTeamToVppMessage = ({ onEditVpp }: IAddTeamToVppMessage) => (
<div className={`${baseClass}__enable-vpp-message`}>
<p className={`${baseClass}__enable-vpp-title`}>
This team isn&apos;t added to Volume Purchasing Program (VPP)
</p>
<p className={`${baseClass}__enable-vpp-description`}>
To add App Store apps, first add this team to VPP.
</p>
<Button onClick={onEditVpp}>Edit VPP</Button>
</div>
);
const NoVppAppsMessage = () => (
<div className={`${baseClass}__no-vpp-message`}>
<p className={`${baseClass}__no-vpp-title`}>
You don&apos;t have any App Store apps
</p>
<p className={`${baseClass}__no-vpp-description`}>
You must purchase apps in{" "}
<CustomLink
url={`${LEARN_MORE_ABOUT_BASE_LINK}/abm-apps`}
text="ABM"
newTab
/>
.<br />
App Store apps that are already added to this team are not listed.
</p>
</div>
);
interface ISoftwareAppStoreProps {
currentTeamId: number;
router: InjectedRouter;
}
const SoftwareAppStoreVpp = ({
currentTeamId,
router,
}: ISoftwareAppStoreProps) => {
const { renderFlash } = useContext(NotificationContext);
const { isPremiumTier } = useContext(AppContext);
const [isLoading, setIsLoading] = useState(false);
const [
showPreviewEndUserExperience,
setShowPreviewEndUserExperience,
] = useState(false);
const {
data: vppInfo,
isLoading: isLoadingVppInfo,
error: errorVppInfo,
} = useQuery<IGetVppTokensResponse, AxiosError>(
["vppInfo", currentTeamId],
() => mdmAppleAPI.getVppTokens(),
{
...DEFAULT_USE_QUERY_OPTIONS,
staleTime: 30000,
retry: (tries, error) => error.status !== 404 && tries <= 3,
}
);
const {
data: labels,
isLoading: isLoadingLabels,
isError: isErrorLabels,
} = useQuery<ILabelSummary[], Error>(
["custom_labels"],
() => labelsAPI.summary().then((res) => getCustomLabels(res.labels)),
{
...DEFAULT_USE_QUERY_OPTIONS,
enabled: isPremiumTier,
staleTime: 10000,
}
);
const noVppTokenUploaded = !vppInfo || !vppInfo.vpp_tokens.length;
const hasVppToken = teamHasVPPToken(currentTeamId, vppInfo?.vpp_tokens);
const {
data: vppApps,
isLoading: isLoadingVppApps,
error: errorVppApps,
} = useQuery(
["vppSoftware", currentTeamId],
() => mdmAppleAPI.getVppApps(currentTeamId),
{
...DEFAULT_USE_QUERY_OPTIONS,
enabled: hasVppToken,
staleTime: 30000,
select: (res) => res.app_store_apps,
}
);
const goBackToSoftwareTitles = (showAvailableForInstallOnly = false) => {
const queryParams = {
team_id: currentTeamId,
...(showAvailableForInstallOnly && { available_for_install: true }),
};
router.push(getPathWithQueryParams(PATHS.SOFTWARE_TITLES, queryParams));
};
const onClickPreviewEndUserExperience = () => {
setShowPreviewEndUserExperience(!showPreviewEndUserExperience);
};
const onAddSoftware = async (formData: ISoftwareVppFormData) => {
if (!formData.selectedApp) {
return;
}
setIsLoading(true);
try {
const {
software_title_id: softwareVppTitleId,
} = await mdmAppleAPI.addVppApp(currentTeamId, formData);
renderFlash(
"success",
<>
<b>{formData.selectedApp.name}</b> successfully added.
</>,
{ persistOnPageChange: true }
);
router.push(
getPathWithQueryParams(
PATHS.SOFTWARE_TITLE_DETAILS(softwareVppTitleId.toString()),
{ team_id: currentTeamId }
)
);
} catch (e) {
renderFlash("error", getErrorMessage(e));
}
setIsLoading(false);
};
const renderContent = () => {
if (!isPremiumTier) {
return (
<PremiumFeatureMessage className={`${baseClass}__premium-message`} />
);
}
if (isLoadingVppInfo || isLoadingVppApps || isLoadingLabels) {
return <Spinner />;
}
if (errorVppInfo || errorVppApps || isErrorLabels) {
return <DataError verticalPaddingSize="pad-xxxlarge" />;
}
if (noVppTokenUploaded) {
return (
<EnableVppMessage
onEnableVpp={() => router.push(PATHS.ADMIN_INTEGRATIONS_VPP)}
/>
);
}
if (!hasVppToken) {
return (
<AddTeamToVppMessage
onEditVpp={() => router.push(PATHS.ADMIN_INTEGRATIONS_VPP)}
/>
);
}
if (!vppApps) {
return <NoVppAppsMessage />;
}
return (
<div className={`${baseClass}__content`}>
<SoftwareVppForm
labels={labels || []}
onSubmit={onAddSoftware}
onCancel={goBackToSoftwareTitles}
onClickPreviewEndUserExperience={onClickPreviewEndUserExperience}
isLoading={isLoading}
vppApps={vppApps}
/>
{showPreviewEndUserExperience && (
<CategoriesEndUserExperienceModal
onCancel={onClickPreviewEndUserExperience}
/>
)}
</div>
);
};
return <div className={baseClass}>{renderContent()}</div>;
};
export default SoftwareAppStoreVpp;