fleet/frontend/pages/ManageControlsPage/OSUpdates/components/TargetSection/TargetSection.tsx
Scott Gress 1915e7122f
Add "update new hosts to latest" to OS Updates form for MacOS (#37103)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #36088

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [X] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

## Testing

- [X] Added/updated automated tests
- [X] QA'd all new/changed functionality manually
Saving this value currently results in a 400 response from the server
since it's not a valid key yet. We can keep this in draft until the
backend is merged if we want to e2e test with it.

### Controls -> OS Settings "Target" section

#### All platforms

- [X] Update success banner message to "Successfully updated."

<img width="200" alt="image"
src="https://github.com/user-attachments/assets/bc43ec79-41d1-4dd3-947c-8152051fd209"
/>

#### macOS / iOS / iPadOS

- [X] Update tooltip text for "Minimum version" to `Enrolled hosts are
updated to exactly this version.`

<img width="250" alt="Image"
src="https://github.com/user-attachments/assets/7d870224-395e-4bc9-937e-be599da57a97"
/>

- [X] Make "available from Apple" a link, replacing "Learn more", and
link to https://fleetdm.com/learn-more-about/apple-available-os-updates

<img width="250" height="363" alt="image"
src="https://github.com/user-attachments/assets/8191ec2d-bf0a-4cf6-9b1a-1272c0ff69b0"
/>

> Note - this URL is current a 404

- [X] Remove text referring to platform from "End user experience"
heading, i.e. it should just say "End user experience" for all platforms
where it appears, not e.g. "End user experience on macOS"

#### macOS Only

- [X] Add new "Update new hosts to latest" checkbox
<img width="316" height="406" alt="Image"
src="https://github.com/user-attachments/assets/71aec05a-b809-436d-8bfd-cd3e14b27ea1"
/>

- [X] Reflects the `macos_updates.update_new_hosts` setting for the team
or (for no team) global config (only testable via automated tests right
now)
- [X] Update End user experience text to "When a minimum version is
enforced, end users see a native macOS notification (DDM) once per day."
(see above)

### Global activity feed

- [X] When "Update new hosts to latest" is enabled, activity should say
`[Actor's name] enabled OS updates for all new macOS hosts on the [team
name] team. macOS hosts will upgrade to the lastest version when they
enroll.`
- [X] When "Update new hosts to latest" is disabled, activity should say
`[Actor's name] disabled updates for all new macOS hosts on the [team
name] team.`

(tested via automated tests)
2025-12-12 11:46:07 -06:00

228 lines
5.9 KiB
TypeScript

import React from "react";
import { API_NO_TEAM_ID, ITeamConfig } from "interfaces/team";
import { IConfig } from "interfaces/config";
import { ApplePlatform } from "interfaces/platform";
import Spinner from "components/Spinner";
import WindowsTargetForm from "../WindowsTargetForm";
import PlatformTabs from "../PlatformTabs";
import { OSUpdatesTargetPlatform } from "../../OSUpdates";
const baseClass = "os-updates-target-section";
type GetDefaultFnParams = {
osType?: ApplePlatform;
currentTeamId: number;
appConfig: IConfig;
teamConfig?: ITeamConfig;
};
const getDefaultUpdateNewHosts = ({
osType,
currentTeamId,
appConfig,
teamConfig,
}: GetDefaultFnParams) => {
const mdmData =
currentTeamId === API_NO_TEAM_ID ? appConfig?.mdm : teamConfig?.mdm;
switch (osType) {
case "darwin":
return !!mdmData?.macos_updates.update_new_hosts;
case "ios":
return !!mdmData?.ios_updates.update_new_hosts;
case "ipados":
return !!mdmData?.ipados_updates.update_new_hosts;
default:
return false;
}
};
const getDefaultOSVersion = ({
osType,
currentTeamId,
appConfig,
teamConfig,
}: GetDefaultFnParams) => {
const mdmData =
currentTeamId === API_NO_TEAM_ID ? appConfig?.mdm : teamConfig?.mdm;
switch (osType) {
case "darwin":
return mdmData?.macos_updates.minimum_version ?? "";
case "ios":
return mdmData?.ios_updates.minimum_version ?? "";
case "ipados":
return mdmData?.ipados_updates.minimum_version ?? "";
default:
return "";
}
};
const getDefaultDeadline = ({
osType,
currentTeamId,
appConfig,
teamConfig,
}: GetDefaultFnParams) => {
const mdmData =
currentTeamId === API_NO_TEAM_ID ? appConfig?.mdm : teamConfig?.mdm;
switch (osType) {
case "darwin":
return mdmData?.macos_updates.deadline ?? "";
case "ios":
return mdmData?.ios_updates.deadline ?? "";
case "ipados":
return mdmData?.ipados_updates.deadline ?? "";
default:
return "";
}
};
const getDefaultWindowsDeadlineDays = ({
currentTeamId,
appConfig,
teamConfig,
}: GetDefaultFnParams) => {
return currentTeamId === API_NO_TEAM_ID
? appConfig.mdm.windows_updates.deadline_days?.toString() ?? ""
: teamConfig?.mdm?.windows_updates.deadline_days?.toString() ?? "";
};
const getDefaultWindowsGracePeriodDays = ({
currentTeamId,
appConfig,
teamConfig,
}: GetDefaultFnParams) => {
return currentTeamId === API_NO_TEAM_ID
? appConfig.mdm.windows_updates.grace_period_days?.toString() ?? ""
: teamConfig?.mdm?.windows_updates.grace_period_days?.toString() ?? "";
};
interface ITargetSectionProps {
appConfig: IConfig;
currentTeamId: number;
isFetching: boolean;
selectedPlatform: OSUpdatesTargetPlatform;
teamConfig?: ITeamConfig;
onSelectPlatform: (platform: OSUpdatesTargetPlatform) => void;
refetchAppConfig: () => void;
refetchTeamConfig: () => void;
}
const TargetSection = ({
appConfig,
currentTeamId,
isFetching,
selectedPlatform,
teamConfig,
onSelectPlatform,
refetchAppConfig,
refetchTeamConfig,
}: ITargetSectionProps) => {
if (isFetching) {
return <Spinner />;
}
const isAndroidMdmEnabled = appConfig.mdm.android_enabled_and_configured;
const isAppleMdmEnabled = appConfig.mdm.enabled_and_configured;
const isWindowsMdmEnabled = appConfig.mdm.windows_enabled_and_configured;
const defaultMacOSVersion = getDefaultOSVersion({
osType: "darwin",
currentTeamId,
appConfig,
teamConfig,
});
const defaultMacOSDeadline = getDefaultDeadline({
osType: "darwin",
currentTeamId,
appConfig,
teamConfig,
});
const defaultIOSVersion = getDefaultOSVersion({
osType: "ios",
currentTeamId,
appConfig,
teamConfig,
});
const defaultIOSDeadline = getDefaultDeadline({
osType: "ios",
currentTeamId,
appConfig,
teamConfig,
});
const defaultIPadOSOSVersion = getDefaultOSVersion({
osType: "ipados",
currentTeamId,
appConfig,
teamConfig,
});
const defaultIPadOSDeadline = getDefaultDeadline({
osType: "ipados",
currentTeamId,
appConfig,
teamConfig,
});
const defaultMacOSUpdateNewHosts = getDefaultUpdateNewHosts({
osType: "darwin",
currentTeamId,
appConfig,
teamConfig,
});
const defaultWindowsDeadlineDays = getDefaultWindowsDeadlineDays({
currentTeamId,
appConfig,
teamConfig,
});
const defaultWindowsGracePeriodDays = getDefaultWindowsGracePeriodDays({
currentTeamId,
appConfig,
teamConfig,
});
const renderTargetForms = () => {
if (isWindowsMdmEnabled && !isAppleMdmEnabled && !isAndroidMdmEnabled) {
return (
<WindowsTargetForm
currentTeamId={currentTeamId}
defaultDeadlineDays={defaultWindowsDeadlineDays}
defaultGracePeriodDays={defaultWindowsGracePeriodDays}
refetchAppConfig={refetchAppConfig}
refetchTeamConfig={refetchTeamConfig}
/>
);
}
return (
<PlatformTabs
currentTeamId={currentTeamId}
defaultMacOSVersion={defaultMacOSVersion}
defaultMacOSDeadline={defaultMacOSDeadline}
defaultIOSVersion={defaultIOSVersion}
defaultIOSDeadline={defaultIOSDeadline}
defaultIPadOSVersion={defaultIPadOSOSVersion}
defaultIPadOSDeadline={defaultIPadOSDeadline}
defaultWindowsDeadlineDays={defaultWindowsDeadlineDays}
defaultWindowsGracePeriodDays={defaultWindowsGracePeriodDays}
defaultMacOSUpdateNewHosts={defaultMacOSUpdateNewHosts}
selectedPlatform={selectedPlatform}
onSelectPlatform={onSelectPlatform}
refetchAppConfig={refetchAppConfig}
refetchTeamConfig={refetchTeamConfig}
isWindowsMdmEnabled={isWindowsMdmEnabled}
isAndroidMdmEnabled={isAndroidMdmEnabled}
/>
);
};
return <div className={baseClass}>{renderTargetForms()}</div>;
};
export default TargetSection;