mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 13:37:30 +00:00
Feat UI update macos windows setup (#12744)
relates to #12168 Updates the fleet UI so that the macOS mdm setup flow is similar to the windows mdm setup. This includes: **Adding a new macos setup card:**   **Adding a new mac os mdm page on a new URL:**  - [x] Changes file added for user-visible changes in `changes/` or `orbit/changes/`. See [Changes files](https://fleetdm.com/docs/contributing/committing-changes#changes-files) for more information. - [x] Manual QA for all new/changed functionality
This commit is contained in:
parent
e919f32e68
commit
c5a4fa60b7
22 changed files with 512 additions and 139 deletions
1
changes/issue-12168-update-macos-mdm-setup-uo
Normal file
1
changes/issue-12168-update-macos-mdm-setup-uo
Normal file
|
|
@ -0,0 +1 @@
|
|||
- update macos mdm setup UI in fleet UI
|
||||
16
frontend/__mocks__/appleMdm.ts
Normal file
16
frontend/__mocks__/appleMdm.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { IMdmApple } from "interfaces/mdm";
|
||||
|
||||
const DEFAULT_MDM_APPLE_MOCK: IMdmApple = {
|
||||
common_name: "APSP:12345",
|
||||
serial_number: "12345",
|
||||
issuer: "Test Certification Authority",
|
||||
renew_date: "2023-03-24T22:13:59Z",
|
||||
};
|
||||
|
||||
export const createMockMdmApple = (
|
||||
overrides?: Partial<IMdmApple>
|
||||
): IMdmApple => {
|
||||
return { ...DEFAULT_MDM_APPLE_MOCK, ...overrides };
|
||||
};
|
||||
|
||||
export default createMockMdmApple;
|
||||
14
frontend/__mocks__/axiosError.ts
Normal file
14
frontend/__mocks__/axiosError.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import { AxiosError } from "axios";
|
||||
|
||||
const DEFAULT_AXIOS_ERROR_MOCK: AxiosError = {
|
||||
isAxiosError: true,
|
||||
toJSON: () => ({}),
|
||||
name: "Error",
|
||||
message: "error message",
|
||||
};
|
||||
|
||||
const createMockAxiosError = (overrides?: Partial<AxiosError>): AxiosError => {
|
||||
return { ...DEFAULT_AXIOS_ERROR_MOCK, ...overrides };
|
||||
};
|
||||
|
||||
export default createMockAxiosError;
|
||||
|
|
@ -18,7 +18,7 @@ const mockRouter = {
|
|||
};
|
||||
|
||||
describe("Integrations Page", () => {
|
||||
it("renders the MDM section in the side nav if MDM feature is enabled", () => {
|
||||
it("renders the MDM sidenav and content if MDM feature is enabled", () => {
|
||||
const render = createCustomRenderer({
|
||||
withBackendMock: true,
|
||||
context: {
|
||||
|
|
@ -30,8 +30,8 @@ describe("Integrations Page", () => {
|
|||
<IntegrationsPage router={mockRouter} params={{ section: "mdm" }} />
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.getByText("Mobile device management (MDM)")
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getAllByText("Mobile device management (MDM)")).toHaveLength(
|
||||
2
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,171 @@
|
|||
import React, { useState } from "react";
|
||||
import { useQuery } from "react-query";
|
||||
import { AxiosError } from "axios";
|
||||
|
||||
import PATHS from "router/paths";
|
||||
import mdmAppleAPI from "services/entities/mdm_apple";
|
||||
import { IMdmApple } from "interfaces/mdm";
|
||||
import { readableDate } from "utilities/helpers";
|
||||
|
||||
import BackLink from "components/BackLink";
|
||||
import MainContent from "components/MainContent";
|
||||
import Button from "components/buttons/Button";
|
||||
import CustomLink from "components/CustomLink";
|
||||
import DataError from "components/DataError";
|
||||
import Spinner from "components/Spinner";
|
||||
import RequestCSRModal from "../components/RequestCSRModal";
|
||||
|
||||
const baseClass = "mac-os-mdm-page";
|
||||
|
||||
interface IApplePuushCertificatePortalSetupProps {
|
||||
onClickRequest: () => void;
|
||||
}
|
||||
|
||||
const ApplePushCertificatePortalSetup = ({
|
||||
onClickRequest,
|
||||
}: IApplePuushCertificatePortalSetupProps) => {
|
||||
return (
|
||||
<div className={`${baseClass}__page-content ${baseClass}__setup-content`}>
|
||||
<p className={`${baseClass}__setup-description`}>
|
||||
Connect Fleet to Apple Push Certificates Portal to change settings and
|
||||
install software on your macOS hosts.
|
||||
</p>
|
||||
<ol className={`${baseClass}__setup-instructions-list`}>
|
||||
<li>
|
||||
<p>
|
||||
1. Request a certificate signing request (CSR) and key for Apple
|
||||
Push Notification Service (APNs) and a certificate and key for
|
||||
Simple Certificate Enrollment Protocol (SCEP).
|
||||
</p>
|
||||
<Button
|
||||
className={`${baseClass}__request-button`}
|
||||
onClick={onClickRequest}
|
||||
variant="brand"
|
||||
>
|
||||
Request
|
||||
</Button>
|
||||
</li>
|
||||
<li>
|
||||
<p>2. Go to your email to download your CSR.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
3.{" "}
|
||||
<CustomLink
|
||||
url="https://identity.apple.com/pushcert/"
|
||||
text="Sign in to Apple Push Certificates Portal"
|
||||
newTab
|
||||
/>
|
||||
<br />
|
||||
If you don't have an Apple ID, select <b>Create yours now</b>.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
4. In Apple Push Certificates Portal, select{" "}
|
||||
<b>Create a Certificate</b>, upload your CSR, and download your APNs
|
||||
certificate.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
5. Deploy Fleet with <b>mdm</b> configuration.{" "}
|
||||
<CustomLink
|
||||
url="https://fleetdm.com/docs/deploying/configuration#mobile-device-management-mdm"
|
||||
text="See how"
|
||||
newTab
|
||||
/>
|
||||
</p>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface IApplePushCertificatePortalSetupInfoProps {
|
||||
appleAPNInfo: IMdmApple;
|
||||
}
|
||||
|
||||
const ApplePushCertificatePortalSetupInfo = ({
|
||||
appleAPNInfo,
|
||||
}: IApplePushCertificatePortalSetupInfoProps) => {
|
||||
return (
|
||||
<dl className={`${baseClass}__page-content ${baseClass}__apc-info`}>
|
||||
<div>
|
||||
<dt>Common name (CN)</dt>
|
||||
<dd>{appleAPNInfo.common_name}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Serial number</dt>
|
||||
<dd>{appleAPNInfo.serial_number}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Issuer</dt>
|
||||
<dd>{appleAPNInfo.issuer}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Renew date</dt>
|
||||
<dd>{readableDate(appleAPNInfo.renew_date)}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
);
|
||||
};
|
||||
|
||||
const MacOSMdmPage = () => {
|
||||
const [showRequestCSRModal, setShowRequestCSRModal] = useState(false);
|
||||
|
||||
// Currently the status of this API call is what determines various UI states on
|
||||
// this page. Because of this we will not render any of this components UI until this API
|
||||
// call has completed.
|
||||
const {
|
||||
data: appleAPNInfo,
|
||||
isLoading: isLoadingMdmApple,
|
||||
error: errorMdmApple,
|
||||
} = useQuery<IMdmApple, AxiosError, IMdmApple>(["appleAPNInfo"], () =>
|
||||
mdmAppleAPI.getAppleAPNInfo()
|
||||
);
|
||||
|
||||
const toggleRequestCSRModal = () => {
|
||||
setShowRequestCSRModal((prevState) => !prevState);
|
||||
};
|
||||
|
||||
const renderPageContent = () => {
|
||||
// The API returns a 404 error if APNs is not configured yet, in that case we
|
||||
// want to prompt the user to download the certs and keys to configure the
|
||||
// server instead of the default error message.
|
||||
const showMdmAppleError = errorMdmApple && errorMdmApple.status !== 404;
|
||||
|
||||
if (showMdmAppleError) {
|
||||
return <DataError />;
|
||||
}
|
||||
|
||||
if (!appleAPNInfo) {
|
||||
return (
|
||||
<ApplePushCertificatePortalSetup
|
||||
onClickRequest={toggleRequestCSRModal}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <ApplePushCertificatePortalSetupInfo appleAPNInfo={appleAPNInfo} />;
|
||||
};
|
||||
|
||||
return (
|
||||
<MainContent className={baseClass}>
|
||||
<>
|
||||
<BackLink
|
||||
text="Back to MDM"
|
||||
path={PATHS.ADMIN_INTEGRATIONS_MDM}
|
||||
className={`${baseClass}__back-to-mdm`}
|
||||
/>
|
||||
<h1>Apple Push Certificate Portal</h1>
|
||||
{isLoadingMdmApple ? <Spinner /> : renderPageContent()}
|
||||
{showRequestCSRModal && (
|
||||
<RequestCSRModal onCancel={toggleRequestCSRModal} />
|
||||
)}
|
||||
</>
|
||||
</MainContent>
|
||||
);
|
||||
};
|
||||
|
||||
export default MacOSMdmPage;
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
.mac-os-mdm-page {
|
||||
&__back-to-mdm {
|
||||
margin-bottom: $pad-xlarge;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-bottom: $pad-xxlarge;
|
||||
font-size: $x-large;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: $x-small;
|
||||
margin: 0 0 $pad-large;
|
||||
}
|
||||
|
||||
&__page-content {
|
||||
font-size: $x-small;
|
||||
}
|
||||
|
||||
&__setup-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $pad-large;
|
||||
color: $core-fleet-black;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__setup-instructions-list {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $pad-large;
|
||||
};
|
||||
|
||||
&__request-button {
|
||||
margin-top: $pad-small;
|
||||
}
|
||||
|
||||
&__apc-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $pad-medium;
|
||||
|
||||
dt {
|
||||
font-weight: $bold;
|
||||
margin-bottom: $pad-xsmall;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from "./MacOSMdmPage";
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useContext, useState } from "react";
|
||||
import React, { useContext } from "react";
|
||||
import { useQuery } from "react-query";
|
||||
import { AxiosError } from "axios";
|
||||
import { InjectedRouter } from "react-router";
|
||||
|
|
@ -8,16 +8,12 @@ import { AppContext } from "context/app";
|
|||
import mdmAppleAPI from "services/entities/mdm_apple";
|
||||
import { IMdmApple } from "interfaces/mdm";
|
||||
|
||||
import { readableDate } from "utilities/helpers";
|
||||
import PATHS from "router/paths";
|
||||
|
||||
import Button from "components/buttons/Button";
|
||||
import CustomLink from "components/CustomLink";
|
||||
import Spinner from "components/Spinner";
|
||||
import DataError from "components/DataError";
|
||||
import RequestCSRModal from "./components/RequestCSRModal";
|
||||
import EndUserMigrationSection from "./components/EndUserMigrationSection/EndUserMigrationSection";
|
||||
import WindowsMdmSection from "./components/WindowsMdmSection/WindowsMdmSection";
|
||||
import WindowsMdmCard from "./components/WindowsMdmCard/WindowsMdmCard";
|
||||
import MacOSMdmCard from "./components/MacOSMdmCard/MacOSMdmCard";
|
||||
|
||||
const baseClass = "mdm-settings";
|
||||
|
||||
|
|
@ -28,8 +24,9 @@ interface IMdmSettingsProps {
|
|||
const MdmSettings = ({ router }: IMdmSettingsProps) => {
|
||||
const { isPremiumTier, config } = useContext(AppContext);
|
||||
|
||||
const [showRequestCSRModal, setShowRequestCSRModal] = useState(false);
|
||||
|
||||
// Currently the status of this API call is what determines various UI states on
|
||||
// this page. Because of this we will not render any of this components UI until this API
|
||||
// call has completed.
|
||||
const {
|
||||
data: appleAPNInfo,
|
||||
isLoading: isLoadingMdmApple,
|
||||
|
|
@ -44,111 +41,42 @@ const MdmSettings = ({ router }: IMdmSettingsProps) => {
|
|||
}
|
||||
);
|
||||
|
||||
const toggleRequestCSRModal = () => {
|
||||
setShowRequestCSRModal(!showRequestCSRModal);
|
||||
const navigateToMacOSMdm = () => {
|
||||
router.push(PATHS.ADMIN_INTEGRATIONS_MDM_MAC);
|
||||
};
|
||||
|
||||
const navigateToWindowsMdm = () => {
|
||||
router.push(PATHS.ADMIN_INTEGRATIONS_MDM_WINDOWS);
|
||||
};
|
||||
|
||||
// The API returns a 404 error if APNs is not configured yet, in that case we
|
||||
// want to prompt the user to download the certs and keys to configure the
|
||||
// server instead of the default error message.
|
||||
const showMdmAppleError = errorMdmApple && errorMdmApple.status !== 404;
|
||||
|
||||
const renderMdmAppleSection = () => {
|
||||
if (showMdmAppleError) {
|
||||
return <DataError />;
|
||||
}
|
||||
|
||||
if (!appleAPNInfo) {
|
||||
return (
|
||||
<>
|
||||
<div className={`${baseClass}__section-description`}>
|
||||
Connect Fleet to Apple Push Certificates Portal to change settings
|
||||
and install software on your macOS hosts.
|
||||
</div>
|
||||
<div className={`${baseClass}__section-instructions`}>
|
||||
<p>
|
||||
1. Request a certificate signing request (CSR) and key for Apple
|
||||
Push Notification Service (APNs) and a certificate and key for
|
||||
Simple Certificate Enrollment Protocol (SCEP).
|
||||
</p>
|
||||
<Button onClick={toggleRequestCSRModal} variant="brand">
|
||||
Request
|
||||
</Button>
|
||||
<p>2. Go to your email to download your CSR.</p>
|
||||
<p>
|
||||
3.{" "}
|
||||
<CustomLink
|
||||
url="https://identity.apple.com/pushcert/"
|
||||
text="Sign in to Apple Push Certificates Portal"
|
||||
newTab
|
||||
/>
|
||||
<br />
|
||||
If you don’t have an Apple ID, select <b>Create yours now</b>.
|
||||
</p>
|
||||
<p>
|
||||
4. In Apple Push Certificates Portal, select{" "}
|
||||
<b>Create a Certificate</b>, upload your CSR, and download your
|
||||
APNs certificate.
|
||||
</p>
|
||||
<p>
|
||||
5. Deploy Fleet with <b>mdm</b> configuration.{" "}
|
||||
<CustomLink
|
||||
url="https://fleetdm.com/docs/deploying/configuration#mobile-device-management-mdm"
|
||||
text="See how"
|
||||
newTab
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={`${baseClass}__section-description`}>
|
||||
To change settings and install software on your macOS hosts, Apple
|
||||
Inc. requires an Apple Push Notification service (APNs) certificate.
|
||||
</div>
|
||||
<div className={`${baseClass}__section-information`}>
|
||||
<h4>Common name (CN)</h4>
|
||||
<p>{appleAPNInfo.common_name}</p>
|
||||
<h4>Serial number</h4>
|
||||
<p>{appleAPNInfo.serial_number}</p>
|
||||
<h4>Issuer</h4>
|
||||
<p>{appleAPNInfo.issuer}</p>
|
||||
<h4>Renew date</h4>
|
||||
<p>{readableDate(appleAPNInfo.renew_date)}</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<div className={`${baseClass}__section`}>
|
||||
<h2>Apple Push Certificates Portal</h2>
|
||||
{isLoadingMdmApple ? <Spinner /> : renderMdmAppleSection()}
|
||||
<div className={`${baseClass}__section ${baseClass}__mdm-section`}>
|
||||
<h2>Mobile device management (MDM)</h2>
|
||||
{isLoadingMdmApple ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
<>
|
||||
<MacOSMdmCard
|
||||
appleAPNInfo={appleAPNInfo}
|
||||
errorData={errorMdmApple}
|
||||
turnOnMacOSMdm={navigateToMacOSMdm}
|
||||
viewDetails={navigateToMacOSMdm}
|
||||
/>
|
||||
{/* TODO: remove conditional rendering when windows MDM is released. */}
|
||||
{config?.mdm_enabled && (
|
||||
<WindowsMdmCard
|
||||
turnOnWindowsMdm={navigateToWindowsMdm}
|
||||
editWindowsMdm={navigateToWindowsMdm}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{/* TODO: remove conditional rendering when windows MDM is released. */}
|
||||
{config?.mdm_enabled && (
|
||||
<WindowsMdmSection
|
||||
turnOnWindowsMdm={navigateToWindowsMdm}
|
||||
editWindowsMdm={navigateToWindowsMdm}
|
||||
/>
|
||||
)}
|
||||
{isPremiumTier && (
|
||||
<>
|
||||
<div className={`${baseClass}__section`}>
|
||||
<EndUserMigrationSection router={router} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{showRequestCSRModal && (
|
||||
<RequestCSRModal onCancel={toggleRequestCSRModal} />
|
||||
{isPremiumTier && appleAPNInfo && (
|
||||
<div className={`${baseClass}__section`}>
|
||||
<EndUserMigrationSection router={router} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
h1 {
|
||||
margin-bottom: $pad-xxlarge;
|
||||
font-size: $x-large;
|
||||
}
|
||||
|
||||
p {
|
||||
|
|
|
|||
|
|
@ -5,20 +5,15 @@
|
|||
gap: 80px;
|
||||
|
||||
&__section {
|
||||
margin: 0 0 $pad-large;
|
||||
|
||||
h2 {
|
||||
margin-bottom: 0;
|
||||
padding-bottom: $pad-small;
|
||||
max-width: 100%;
|
||||
font-size: $medium;
|
||||
font-weight: $regular;
|
||||
color: $core-fleet-black;
|
||||
border-bottom: solid 1px $ui-fleet-black-10;
|
||||
margin: 0 0 $pad-xxlarge;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.mdm-settings-team-btn {
|
||||
|
|
@ -34,17 +29,9 @@
|
|||
}
|
||||
}
|
||||
|
||||
&__section-description,
|
||||
&__section-instructions,
|
||||
&__section-information {
|
||||
font-size: $x-small;
|
||||
color: $core-fleet-black;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__section-information {
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
&__mdm-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 40px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -139,8 +139,8 @@ const EndUserMigrationSection = ({ router }: IEndUserMigrationSectionProps) => {
|
|||
<div className={baseClass}>
|
||||
<h2>End user migration workflow</h2>
|
||||
<p>
|
||||
Control the end user migration workflow for hosts that automatically
|
||||
enrolled to your old MDM solution.
|
||||
Control the end user migration workflow for macOS hosts that
|
||||
automatically enrolled to your old MDM solution.
|
||||
</p>
|
||||
|
||||
<img
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
import React from "react";
|
||||
import { noop } from "lodash";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
|
||||
import createMockMdmApple from "__mocks__/appleMdm";
|
||||
import createMockAxiosError from "__mocks__/axiosError";
|
||||
|
||||
import MacOSMdmCard from "./MacOSMdmCard";
|
||||
|
||||
describe("MacOSMdmCard", () => {
|
||||
it("renders the turn on macOs mdm state when there is no appleAPNInfo", () => {
|
||||
render(
|
||||
<MacOSMdmCard
|
||||
appleAPNInfo={undefined}
|
||||
errorData={null}
|
||||
turnOnMacOSMdm={noop}
|
||||
viewDetails={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText("Turn on macOS MDM")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders the show details state when there is appleAPNInfo", () => {
|
||||
render(
|
||||
<MacOSMdmCard
|
||||
appleAPNInfo={createMockMdmApple()}
|
||||
errorData={null}
|
||||
turnOnMacOSMdm={noop}
|
||||
viewDetails={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText("macOS MDM turned on")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders the error state when there is a non 404 error", () => {
|
||||
render(
|
||||
<MacOSMdmCard
|
||||
appleAPNInfo={createMockMdmApple()}
|
||||
errorData={createMockAxiosError({ status: 500 })}
|
||||
turnOnMacOSMdm={noop}
|
||||
viewDetails={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText(/Something's gone wrong/)).toBeInTheDocument();
|
||||
|
||||
render(
|
||||
<MacOSMdmCard
|
||||
appleAPNInfo={createMockMdmApple()}
|
||||
errorData={createMockAxiosError({ status: 404 })}
|
||||
turnOnMacOSMdm={noop}
|
||||
viewDetails={noop}
|
||||
/>
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
import React from "react";
|
||||
|
||||
import Button from "components/buttons/Button";
|
||||
import Icon from "components/Icon";
|
||||
import Card from "components/Card";
|
||||
import DataError from "components/DataError";
|
||||
import { AxiosError } from "axios";
|
||||
import { IMdmApple } from "interfaces/mdm";
|
||||
|
||||
const baseClass = "mac-os-mdm-card";
|
||||
|
||||
interface ITurnOnMacOSMdmProps {
|
||||
onClickTurnOn: () => void;
|
||||
}
|
||||
|
||||
const TurnOnMacOSMdm = ({ onClickTurnOn }: ITurnOnMacOSMdmProps) => {
|
||||
return (
|
||||
<div className={`${baseClass}__turn-on-mac-os`}>
|
||||
<div>
|
||||
<h3>Turn on macOS MDM</h3>
|
||||
<p>
|
||||
Connect Fleet to Apple Push Certificates Portal to change settings and
|
||||
install software on your macOS hosts.
|
||||
</p>
|
||||
</div>
|
||||
<Button onClick={onClickTurnOn}>Connect APNS</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface ITurnOffMacOSMdmProps {
|
||||
onClickDetails: () => void;
|
||||
}
|
||||
|
||||
const SeeDetailsMacOSMdm = ({ onClickDetails }: ITurnOffMacOSMdmProps) => {
|
||||
return (
|
||||
<div className={`${baseClass}__turn-off-mac-os`}>
|
||||
<div>
|
||||
<Icon name="success" />
|
||||
<p>macOS MDM turned on</p>
|
||||
</div>
|
||||
<Button onClick={onClickDetails} variant="text-icon">
|
||||
Details
|
||||
<Icon name="chevron" direction="right" color="core-fleet-blue" />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface IMacOSMdmCardProps {
|
||||
appleAPNInfo: IMdmApple | undefined;
|
||||
errorData: AxiosError | null;
|
||||
turnOnMacOSMdm: () => void;
|
||||
viewDetails: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* This compoent is responsible for showing the correct UI for the macOS MDM card.
|
||||
* We pass in the appleAPNInfo and errorData from the MdmSettings component because
|
||||
* we need to make that API call higher up in the component tree to correctly show
|
||||
* loading states on the page.
|
||||
*/
|
||||
const MacOSMdmCard = ({
|
||||
appleAPNInfo,
|
||||
errorData,
|
||||
turnOnMacOSMdm,
|
||||
viewDetails,
|
||||
}: IMacOSMdmCardProps) => {
|
||||
// The API returns a 404 error if APNS is not configured yet. If there is any
|
||||
// other error we will show the DataError component.
|
||||
const showError = errorData !== null && errorData.status !== 404;
|
||||
|
||||
if (showError) {
|
||||
return <DataError />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className={baseClass} color="gray">
|
||||
{appleAPNInfo !== undefined ? (
|
||||
<SeeDetailsMacOSMdm onClickDetails={viewDetails} />
|
||||
) : (
|
||||
<TurnOnMacOSMdm onClickTurnOn={turnOnMacOSMdm} />
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default MacOSMdmCard;
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
.mac-os-mdm-card {
|
||||
font-size: $x-small;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&__turn-on-mac-os,
|
||||
&__turn-off-mac-os {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__turn-on-mac-os {
|
||||
h3 {
|
||||
font-size: $x-small;
|
||||
font-weight: $bold;
|
||||
margin: 0 0 $pad-xsmall;
|
||||
}
|
||||
|
||||
p {
|
||||
max-width: 520px;
|
||||
}
|
||||
}
|
||||
|
||||
&__turn-off-mac-os {
|
||||
>div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-left: $pad-small;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from "./MacOSMdmCard";
|
||||
|
|
@ -6,15 +6,16 @@ import Card from "components/Card/Card";
|
|||
import Button from "components/buttons/Button";
|
||||
import Icon from "components/Icon";
|
||||
|
||||
const baseClass = "windows-mdm-section";
|
||||
const baseClass = "windows-mdm-card";
|
||||
|
||||
interface ITurnOnWindowsMdmProps {
|
||||
onClickTurnOn: () => void;
|
||||
}
|
||||
|
||||
const TurnOnWindowsMdm = ({ onClickTurnOn }: ITurnOnWindowsMdmProps) => {
|
||||
return (
|
||||
<div className={`${baseClass}__turn-on-windows`}>
|
||||
<div className={`${baseClass}__`}>
|
||||
<div>
|
||||
<h3>Turn on Windows MDM</h3>
|
||||
<p>Turn MDM on for Windows hosts with fleetd.</p>
|
||||
</div>
|
||||
|
|
@ -42,15 +43,15 @@ const TurnOffWindowsMdm = ({ onClickEdit }: ITurnOffWindowsMdmProps) => {
|
|||
);
|
||||
};
|
||||
|
||||
interface IWindowsMdmSectionProps {
|
||||
interface IWindowsMdmCardProps {
|
||||
turnOnWindowsMdm: () => void;
|
||||
editWindowsMdm: () => void;
|
||||
}
|
||||
|
||||
const WindowsMdmSection = ({
|
||||
const WindowsMdmCard = ({
|
||||
turnOnWindowsMdm,
|
||||
editWindowsMdm,
|
||||
}: IWindowsMdmSectionProps) => {
|
||||
}: IWindowsMdmCardProps) => {
|
||||
const { config } = useContext(AppContext);
|
||||
|
||||
const isWindowsMdmEnabled =
|
||||
|
|
@ -67,4 +68,4 @@ const WindowsMdmSection = ({
|
|||
);
|
||||
};
|
||||
|
||||
export default WindowsMdmSection;
|
||||
export default WindowsMdmCard;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
.windows-mdm-section {
|
||||
.windows-mdm-card {
|
||||
font-size: $x-small;
|
||||
|
||||
p {
|
||||
|
|
@ -29,5 +29,4 @@
|
|||
margin-left: $pad-small;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from "./WindowsMdmCard";
|
||||
|
|
@ -1 +0,0 @@
|
|||
export { default } from "./WindowsMdmSection";
|
||||
|
|
@ -53,7 +53,8 @@ import AgentOptionsPage from "pages/admin/TeamManagementPage/TeamDetailsWrapper/
|
|||
import MacOSUpdates from "pages/ManageControlsPage/MacOSUpdates";
|
||||
import MacOSSettings from "pages/ManageControlsPage/MacOSSettings";
|
||||
import MacOSSetup from "pages/ManageControlsPage/MacOSSetup/MacOSSetup";
|
||||
import WindowsMdmPage from "pages/admin/IntegrationsPage/cards/MdmSettings/WindowsMdmPage/WindowsMdmPage";
|
||||
import WindowsMdmPage from "pages/admin/IntegrationsPage/cards/MdmSettings/WindowsMdmPage";
|
||||
import MacOSMdmPage from "pages/admin/IntegrationsPage/cards/MdmSettings/MacOSMdmPage";
|
||||
|
||||
import PATHS from "router/paths";
|
||||
|
||||
|
|
@ -138,6 +139,7 @@ const routes = (
|
|||
</Route>
|
||||
</Route>
|
||||
<Route path="integrations/mdm/windows" component={WindowsMdmPage} />
|
||||
<Route path="integrations/mdm/apple" component={MacOSMdmPage} />
|
||||
<Route path="teams" component={TeamDetailsWrapper}>
|
||||
<Route path="members" component={MembersPage} />
|
||||
<Route path="options" component={AgentOptionsPage} />
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ export default {
|
|||
ADMIN_INTEGRATIONS: `${URL_PREFIX}/settings/integrations`,
|
||||
ADMIN_INTEGRATIONS_TICKET_DESTINATIONS: `${URL_PREFIX}/settings/integrations/ticket-destinations`,
|
||||
ADMIN_INTEGRATIONS_MDM: `${URL_PREFIX}/settings/integrations/mdm`,
|
||||
ADMIN_INTEGRATIONS_MDM_MAC: `${URL_PREFIX}/settings/integrations/mdm/apple`,
|
||||
ADMIN_INTEGRATIONS_MDM_WINDOWS: `${URL_PREFIX}/settings/integrations/mdm/windows`,
|
||||
ADMIN_INTEGRATIONS_AUTOMATIC_ENROLLMENT: `${URL_PREFIX}/settings/integrations/automatic-enrollment`,
|
||||
ADMIN_TEAMS: `${URL_PREFIX}/settings/teams`,
|
||||
|
|
|
|||
|
|
@ -136,3 +136,12 @@ hr {
|
|||
border: none;
|
||||
border-bottom: 1px solid $ui-fleet-black-10;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin: 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue