Add UI banner when VPP token is about to expire/has expired (#20734)
relate to #19691 Adds a UI banner that conditionally shows on every page (excluding my device page) if the VPP token that was uploaded is within 30 days of expiring. It will also show a warning banner if the token has already expired. **Token about to expire:**  **Token has expired:**  - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. - [ ] Added/updated tests - [c] Manual QA for all new/changed functionality
This commit is contained in:
@@ -0,0 +1 @@
|
||||
- add a warning banner to the UI if the uploaded VPP token is about to expire/has expired.
|
||||
@@ -66,6 +66,7 @@ const App = ({ children, location }: IAppProps): JSX.Element => {
|
||||
setEnrollSecret,
|
||||
setABMExpiry,
|
||||
setAPNsExpiry,
|
||||
setVppExpiry,
|
||||
setSandboxExpiry,
|
||||
setNoSandboxHosts,
|
||||
} = useContext(AppContext);
|
||||
@@ -111,6 +112,12 @@ const App = ({ children, location }: IAppProps): JSX.Element => {
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
try {
|
||||
const vppInfo = await mdmAppleAPI.getVppInfo();
|
||||
setVppExpiry(vppInfo.renew_date);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,11 @@ const InfoBanner = ({
|
||||
);
|
||||
}
|
||||
|
||||
return <div className={wrapperClasses}>{content}</div>;
|
||||
return (
|
||||
<div className={wrapperClasses} role="status">
|
||||
{content}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default InfoBanner;
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
.license-expiry-banner {
|
||||
margin-bottom: $pad-large;
|
||||
position: sticky; // Needed for settings page scroll
|
||||
z-index: 4; // Needed for settings page scroll
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
.apple-bm-renewal-message {
|
||||
margin-bottom: $pad-large;
|
||||
position: sticky; // Needed for settings page scroll
|
||||
z-index: 4; // Needed for settings page scroll
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
.apple-bm-terms-message {
|
||||
margin-bottom: $pad-large;
|
||||
position: sticky; // Needed for settings page scroll
|
||||
z-index: 4; // Needed for settings page scroll
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
.apple-pn-cert-renewal-message {
|
||||
margin-bottom: $pad-large;
|
||||
position: sticky; // Needed for settings page scroll
|
||||
z-index: 4; // Needed for settings page scroll
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import { AppContext } from "context/app";
|
||||
import LicenseExpirationBanner from "components/LicenseExpirationBanner";
|
||||
import ApplePNCertRenewalMessage from "components/MDM/ApplePNCertRenewalMessage";
|
||||
import AppleBMRenewalMessage from "components/MDM/AppleBMRenewalMessage";
|
||||
import VppRenewalMessage from "./banners/VppRenewalMessage";
|
||||
|
||||
interface IMainContentProps {
|
||||
children: ReactNode;
|
||||
@@ -36,8 +37,9 @@ const MainContent = ({
|
||||
config,
|
||||
isPremiumTier,
|
||||
noSandboxHosts,
|
||||
apnsExpiry,
|
||||
abmExpiry,
|
||||
apnsExpiry = "",
|
||||
abmExpiry = "",
|
||||
vppExpiry = "",
|
||||
} = useContext(AppContext);
|
||||
|
||||
const sandboxExpiryTime =
|
||||
@@ -47,39 +49,38 @@ const MainContent = ({
|
||||
|
||||
const renderAppWideBanner = () => {
|
||||
const isAppleBmTermsExpired = config?.mdm?.apple_bm_terms_expired;
|
||||
const isApplePnsExpired = hasLicenseExpired(apnsExpiry || "");
|
||||
const willApplePnsExpireIn30Days = willExpireWithinXDays(
|
||||
apnsExpiry || "",
|
||||
30
|
||||
);
|
||||
const isAppleBmExpired = hasLicenseExpired(abmExpiry || ""); // NOTE: See Rachel's related FIXME added to App.tsx in https://github.com/fleetdm/fleet/pull/19571
|
||||
const willAppleBmExpireIn30Days = willExpireWithinXDays(
|
||||
abmExpiry || "",
|
||||
30
|
||||
);
|
||||
const isApplePnsExpired = hasLicenseExpired(apnsExpiry);
|
||||
const willApplePnsExpireIn30Days = willExpireWithinXDays(apnsExpiry, 30);
|
||||
const isAppleBmExpired = hasLicenseExpired(abmExpiry); // NOTE: See Rachel's related FIXME added to App.tsx in https://github.com/fleetdm/fleet/pull/19571
|
||||
const willAppleBmExpireIn30Days = willExpireWithinXDays(abmExpiry, 30);
|
||||
const isFleetLicenseExpired = hasLicenseExpired(
|
||||
config?.license.expiration || ""
|
||||
);
|
||||
|
||||
const isVppExpired = hasLicenseExpired(vppExpiry);
|
||||
const willVppExpireIn30Days = willExpireWithinXDays(vppExpiry, 30);
|
||||
|
||||
let banner: JSX.Element | null = null;
|
||||
|
||||
if (isPremiumTier) {
|
||||
if (isApplePnsExpired || willApplePnsExpireIn30Days) {
|
||||
return <ApplePNCertRenewalMessage expired={isApplePnsExpired} />;
|
||||
}
|
||||
|
||||
if (isAppleBmExpired || willAppleBmExpireIn30Days) {
|
||||
return <AppleBMRenewalMessage expired={isAppleBmExpired} />;
|
||||
}
|
||||
|
||||
if (isAppleBmTermsExpired) {
|
||||
return <AppleBMTermsMessage />;
|
||||
}
|
||||
|
||||
if (isFleetLicenseExpired) {
|
||||
return <LicenseExpirationBanner />;
|
||||
banner = <ApplePNCertRenewalMessage expired={isApplePnsExpired} />;
|
||||
} else if (isAppleBmExpired || willAppleBmExpireIn30Days) {
|
||||
banner = <AppleBMRenewalMessage expired={isAppleBmExpired} />;
|
||||
} else if (isAppleBmTermsExpired) {
|
||||
banner = <AppleBMTermsMessage />;
|
||||
} else if (isFleetLicenseExpired) {
|
||||
banner = <LicenseExpirationBanner />;
|
||||
} else if (isVppExpired || willVppExpireIn30Days) {
|
||||
banner = <VppRenewalMessage expired={isVppExpired} />;
|
||||
}
|
||||
}
|
||||
|
||||
return <></>;
|
||||
if (banner) {
|
||||
return <div className={`${baseClass}__warning-banner`}>{banner}</div>;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
return (
|
||||
<div className={classes}>
|
||||
|
||||
@@ -10,4 +10,11 @@
|
||||
> :not(.main-content--animation-disabled) {
|
||||
animation: fade-in 250ms ease-out;
|
||||
}
|
||||
|
||||
&__warning-banner {
|
||||
margin-bottom: $pad-large;
|
||||
// TODO: figure out if sticky styling is needed?
|
||||
position: sticky; // Needed for settings page scroll
|
||||
z-index: 4; // Needed for settings page scroll
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import React from "react";
|
||||
|
||||
import CustomLink from "components/CustomLink";
|
||||
import InfoBanner from "components/InfoBanner";
|
||||
|
||||
const baseClass = "vpp-renewal-message";
|
||||
|
||||
interface IVppRenewalMessageProps {
|
||||
expired: boolean;
|
||||
}
|
||||
|
||||
const VppRenewalMessage = ({ expired }: IVppRenewalMessageProps) => {
|
||||
return (
|
||||
<InfoBanner
|
||||
className={baseClass}
|
||||
color="yellow"
|
||||
cta={
|
||||
<CustomLink
|
||||
url="https://fleetdm.com/learn-more-about/renew-vpp"
|
||||
text="Renew VPP"
|
||||
className={`${baseClass}__new-tab`}
|
||||
newTab
|
||||
color="core-fleet-black"
|
||||
iconColor="core-fleet-black"
|
||||
/>
|
||||
}
|
||||
>
|
||||
{expired ? (
|
||||
<>
|
||||
Your Volume Purchasing Program (VPP) content token has expired. You
|
||||
can't add or install App Store apps.
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
Your Volume Purchasing Program (VPP) content token is less than 30
|
||||
days from expiration. If it expires, you won't be able to add or
|
||||
install App Store apps.
|
||||
</>
|
||||
)}
|
||||
</InfoBanner>
|
||||
);
|
||||
};
|
||||
|
||||
export default VppRenewalMessage;
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./VppRenewalMessage";
|
||||
@@ -20,6 +20,7 @@ enum ACTIONS {
|
||||
SET_ENROLL_SECRET = "SET_ENROLL_SECRET",
|
||||
SET_ABM_EXPIRY = "SET_ABM_EXPIRY",
|
||||
SET_APNS_EXPIRY = "SET_APNS_EXPIRY",
|
||||
SET_VPP_EXPIRY = "SET_VPP_EXPIRY",
|
||||
SET_SANDBOX_EXPIRY = "SET_SANDBOX_EXPIRY",
|
||||
SET_NO_SANDBOX_HOSTS = "SET_NO_SANDBOX_HOSTS",
|
||||
SET_FILTERED_HOSTS_PATH = "SET_FILTERED_HOSTS_PATH",
|
||||
@@ -62,6 +63,11 @@ interface ISetAPNsExpiryAction {
|
||||
apnsExpiry: string;
|
||||
}
|
||||
|
||||
interface ISetVppExpiryAction {
|
||||
type: ACTIONS.SET_VPP_EXPIRY;
|
||||
vppExpiry: string;
|
||||
}
|
||||
|
||||
interface ISetSandboxExpiryAction {
|
||||
type: ACTIONS.SET_SANDBOX_EXPIRY;
|
||||
sandboxExpiry: string;
|
||||
@@ -99,6 +105,7 @@ type IAction =
|
||||
| ISetEnrollSecretAction
|
||||
| ISetABMExpiryAction
|
||||
| ISetAPNsExpiryAction
|
||||
| ISetVppExpiryAction
|
||||
| ISetSandboxExpiryAction
|
||||
| ISetNoSandboxHostsAction
|
||||
| ISetFilteredHostsPathAction
|
||||
@@ -139,12 +146,14 @@ type InitialStateType = {
|
||||
isNoAccess?: boolean;
|
||||
abmExpiry?: string;
|
||||
apnsExpiry?: string;
|
||||
vppExpiry?: string;
|
||||
sandboxExpiry?: string;
|
||||
noSandboxHosts?: boolean;
|
||||
filteredHostsPath?: string;
|
||||
filteredSoftwarePath?: string;
|
||||
filteredQueriesPath?: string;
|
||||
filteredPoliciesPath?: string;
|
||||
isVppEnabled?: boolean;
|
||||
setAvailableTeams: (
|
||||
user: IUser | null,
|
||||
availableTeams: ITeamSummary[]
|
||||
@@ -155,6 +164,7 @@ type InitialStateType = {
|
||||
setEnrollSecret: (enrollSecret: IEnrollSecret[]) => void;
|
||||
setAPNsExpiry: (apnsExpiry: string) => void;
|
||||
setABMExpiry: (abmExpiry: string) => void;
|
||||
setVppExpiry: (vppExpiry: string) => void;
|
||||
setSandboxExpiry: (sandboxExpiry: string) => void;
|
||||
setNoSandboxHosts: (noSandboxHosts: boolean) => void;
|
||||
setFilteredHostsPath: (filteredHostsPath: string) => void;
|
||||
@@ -203,6 +213,7 @@ export const initialState = {
|
||||
setEnrollSecret: () => null,
|
||||
setAPNsExpiry: () => null,
|
||||
setABMExpiry: () => null,
|
||||
setVppExpiry: () => null,
|
||||
setSandboxExpiry: () => null,
|
||||
setNoSandboxHosts: () => null,
|
||||
setFilteredHostsPath: () => null,
|
||||
@@ -337,6 +348,13 @@ const reducer = (state: InitialStateType, action: IAction) => {
|
||||
apnsExpiry,
|
||||
};
|
||||
}
|
||||
case ACTIONS.SET_VPP_EXPIRY: {
|
||||
const { vppExpiry } = action;
|
||||
return {
|
||||
...state,
|
||||
vppExpiry,
|
||||
};
|
||||
}
|
||||
case ACTIONS.SET_SANDBOX_EXPIRY: {
|
||||
const { sandboxExpiry } = action;
|
||||
return {
|
||||
@@ -399,6 +417,7 @@ const AppProvider = ({ children }: Props): JSX.Element => {
|
||||
sandboxExpiry: state.sandboxExpiry,
|
||||
abmExpiry: state.abmExpiry,
|
||||
apnsExpiry: state.apnsExpiry,
|
||||
vppExpiry: state.vppExpiry,
|
||||
noSandboxHosts: state.noSandboxHosts,
|
||||
filteredHostsPath: state.filteredHostsPath,
|
||||
filteredSoftwarePath: state.filteredSoftwarePath,
|
||||
@@ -450,6 +469,12 @@ const AppProvider = ({ children }: Props): JSX.Element => {
|
||||
setAPNsExpiry: (apnsExpiry: string) => {
|
||||
dispatch({ type: ACTIONS.SET_APNS_EXPIRY, apnsExpiry });
|
||||
},
|
||||
setVppExpiry: (vppExpiry: string) => {
|
||||
dispatch({
|
||||
type: ACTIONS.SET_VPP_EXPIRY,
|
||||
vppExpiry,
|
||||
});
|
||||
},
|
||||
setSandboxExpiry: (sandboxExpiry: string) => {
|
||||
dispatch({ type: ACTIONS.SET_SANDBOX_EXPIRY, sandboxExpiry });
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user