Files
Andrew Mellor 8e0c038eff 47701 abm errors UI (#49896)
**Related issue:** Resolves #47701

# Checklist for submitter

## Testing

- [x] Added/updated automated tests

- [ ] QA'd all new/changed functionality manually: Not able to test
token_rejected, terms and conditions or apple server error.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added an Apple Business Manager invalid-token warning banner that
lists the affected organization names.
* Introduced dedicated invalid-token state support so the banner can
appear with the correct priority.
* **Bug Fixes**
* Ensured invalid-token state is cleared when no tokens are returned and
consistently set on token fetch success/error.
* Improved Apple/DEP status messaging and made profile-assignment
rendering more resilient for non-DEP and partial-error responses.
* **Style**
* Adjusted banner spacing and added styling for DEP error presentation.
* **Tests**
* Added/expanded tests for invalid-token messaging, ABM expiry updates,
and MDM status/error scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-28 15:01:48 +01:00

110 lines
3.4 KiB
TypeScript

import React, { ReactNode, useContext } from "react";
import classnames from "classnames";
import { hasLicenseExpired } from "utilities/helpers";
import { AppContext } from "context/app";
import AppleBMTermsMessage from "components/MDM/AppleBMTermsMessage";
import AppleBMTokenInvalidMessage from "components/MDM/AppleBMTokenInvalidMessage";
import LicenseExpirationBanner from "components/LicenseExpirationBanner";
import ApplePNCertRenewalMessage from "components/MDM/ApplePNCertRenewalMessage";
import AppleBMRenewalMessage from "components/MDM/AppleBMRenewalMessage";
import AndroidEnterpriseDeletedMessage from "components/MDM/AndroidEnterpriseDeletedMessage";
import VppRenewalMessage from "./banners/VppRenewalMessage";
export interface IMainContentConfig {
renderedBanner: boolean;
}
interface IMainContentProps {
children: ReactNode | ((mainContentConfig: IMainContentConfig) => ReactNode);
/** An optional classname to pass to the main content component.
* This can be used to apply styles directly onto the main content div
*/
className?: string;
}
const baseClass = "main-content";
/**
* A component that controls the layout and styling of the main content region
* of the application.
*/
const MainContent = ({
children,
className,
}: IMainContentProps): JSX.Element => {
const classes = classnames(baseClass, className);
const {
config,
isPremiumTier,
isAndroidEnterpriseDeleted,
isApplePnsExpired,
isAppleBmExpired,
isVppExpired,
needsAbmTermsRenewal,
hasInvalidABMToken,
invalidAbmTokenOrgNames,
willAppleBmExpire,
willApplePnsExpire,
willVppExpire,
} = useContext(AppContext);
const renderAppWideBanner = () => {
const isFleetLicenseExpired = hasLicenseExpired(
config?.license.expiration || ""
);
let banner: JSX.Element | null = null;
// the order of these checks is important. This is the priority order
// for showing banners and only one banner is shown at a time.
if (isApplePnsExpired || willApplePnsExpire) {
// APNs expiration banner will show for either premium or free tiers
// but all other banners are only for premium tiers
banner = <ApplePNCertRenewalMessage expired={isApplePnsExpired} />;
} else if (isPremiumTier) {
if (isAndroidEnterpriseDeleted) {
banner = <AndroidEnterpriseDeletedMessage />;
} else if (isAppleBmExpired || willAppleBmExpire) {
banner = <AppleBMRenewalMessage expired={isAppleBmExpired} />;
} else if (needsAbmTermsRenewal) {
banner = <AppleBMTermsMessage />;
} else if (hasInvalidABMToken) {
banner = (
<AppleBMTokenInvalidMessage orgNames={invalidAbmTokenOrgNames} />
);
} else if (isVppExpired || willVppExpire) {
banner = <VppRenewalMessage expired={isVppExpired} />;
} else if (isFleetLicenseExpired) {
banner = <LicenseExpirationBanner />;
}
}
if (banner) {
return (
<div className={`${baseClass}--animation-disabled`}>
<div className={`${baseClass}__warning-banner`}>{banner}</div>
</div>
);
}
return null;
};
const appWideBanner = renderAppWideBanner();
const mainContentConfig: IMainContentConfig = {
renderedBanner: !!appWideBanner,
};
return (
<div className={classes}>
{appWideBanner}
{typeof children === "function" ? children(mainContentConfig) : children}
</div>
);
};
export default MainContent;