Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f9f219b
Create Pending icon; initial scaffold for macOS settings indicator in…
jacobshandling Feb 9, 2023
7226518
work on mac settings indicator and modal
jacobshandling Feb 10, 2023
c282edf
build until mac settings table scaffold works in modal
jacobshandling Feb 11, 2023
23ddc9f
Conditionally determine icon and tooltip text
jacobshandling Feb 13, 2023
90cf2c9
Create settings indicator
jacobshandling Feb 13, 2023
7b7cdb0
mock mac setting data - will change to fit API
jacobshandling Feb 13, 2023
1d6fc3d
adjust style of mac settings indicator in host summary
jacobshandling Feb 13, 2023
16c5411
make indicator styling more specific; integrate Sarah's feedback
jacobshandling Feb 14, 2023
822b1c1
wire up with planned API structure
jacobshandling Feb 15, 2023
b539bd9
small style and copy changes
jacobshandling Feb 15, 2023
afece10
logic for macsettings status
jacobshandling Feb 15, 2023
f266128
correctly tie tooltips to unique ids
jacobshandling Feb 15, 2023
561b4d4
style adjustments
jacobshandling Feb 15, 2023
b561375
style adjustments; error tooltip
jacobshandling Feb 15, 2023
6980103
change file
jacobshandling Feb 15, 2023
de675a5
Add conditions for displaying macOS settings indicator; misc cleanup
jacobshandling Feb 16, 2023
2696572
update MacSettingsTable to use DEFAULT_EMPTY_CELL_VALUE
jacobshandling Feb 17, 2023
5ca43c3
Retool for new API; gracefully handle unexpected profiles
jacobshandling Feb 21, 2023
270d269
tests
jacobshandling Feb 21, 2023
c6eb53c
remove faulty test
jacobshandling Feb 21, 2023
3a07b59
Merge branch 'main' into add-macOS-settings-items
jacobshandling Feb 21, 2023
e5e8092
specify type name
jacobshandling Feb 21, 2023
06277bf
remove mock host profiles data
jacobshandling Feb 21, 2023
d03de47
remove unnecessary isLoading props and EmptyTable todo
jacobshandling Feb 22, 2023
b4d5f64
remove empty table todo
jacobshandling Feb 22, 2023
878bad5
Adjust modals width
jacobshandling Feb 22, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/9413-macOS-settings-indicator
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Create a macOS settings indicator and modal on the host details and device user pages.
4 changes: 3 additions & 1 deletion frontend/components/DiskSpaceGraph/DiskSpaceGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ interface IDiskSpaceGraphProps {
percentDiskSpaceAvailable: number;
id: string;
platform: string;
tooltipPosition?: "top" | "bottom";
}

const DiskSpaceGraph = ({
Expand All @@ -16,6 +17,7 @@ const DiskSpaceGraph = ({
percentDiskSpaceAvailable,
id,
platform,
tooltipPosition = "top",
}: IDiskSpaceGraphProps): JSX.Element => {
const getDiskSpaceIndicatorColor = (): string => {
// return space-dependent graph colors for mac and windows hosts, green for linux
Expand Down Expand Up @@ -65,7 +67,7 @@ const DiskSpaceGraph = ({
{diskSpaceTooltipText && (
<ReactTooltip
className={"disk-space-tooltip"}
place="top"
place={tooltipPosition}
type="dark"
effect="solid"
id={`tooltip-${id}`}
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/Icon/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import classnames from "classnames";

import { IconNames, ICON_MAP } from "components/icons";
import { Colors } from "styles/var/colors";
import { ICON_SIZES, IconSizes } from "styles/var/icon_sizes";
import { IconSizes } from "styles/var/icon_sizes";

interface IIconProps {
name: IconNames;
Expand Down
3 changes: 2 additions & 1 deletion frontend/components/Modal/_styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@
&__modal_container {
@include position(absolute, 22px null null null);
background-color: $core-white;
width: 570px;
min-width: 650px;
max-width: 800px;
padding: $pad-xxlarge;
border-radius: 8px;
animation: scale-up 150ms ease-out;
Expand Down
3 changes: 2 additions & 1 deletion frontend/components/StatusIndicator/StatusIndicator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ interface IStatusIndicatorProps {
tooltip?: {
id: number;
tooltipText: string;
position?: "top" | "bottom";
};
}

Expand Down Expand Up @@ -40,7 +41,7 @@ const StatusIndicator = ({
</span>
<ReactTooltip
className="status-tooltip"
place="top"
place={tooltip?.position ? tooltip.position : "top"}
type="dark"
effect="solid"
id={`status-${tooltip.id}`}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useState, useRef, useLayoutEffect } from "react";
import { uniqueId } from "lodash";

import ReactTooltip from "react-tooltip";
import { DEFAULT_EMPTY_CELL_VALUE } from "utilities/constants";

interface ITruncatedTextCellProps {
value: string | number | boolean;
Expand All @@ -28,7 +29,7 @@ const TruncatedTextCell = ({

const tooltipId = uniqueId();
const tooltipDisabled = offsetWidth === scrollWidth;

const isDefaultValue = value === DEFAULT_EMPTY_CELL_VALUE;
return (
<div ref={ref} className={`${baseClass} ${classes}`}>
<div
Expand All @@ -39,8 +40,8 @@ const TruncatedTextCell = ({
>
<span
className={`data-table__truncated-text--cell ${
tooltipDisabled ? "" : "truncated"
}`}
isDefaultValue ? "text-muted" : ""
} ${tooltipDisabled ? "" : "truncated"}`}
>
{value}
</span>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
.truncated-cell {
.text-muted {
color: $ui-fleet-black-50;
}
.data-table__truncated-text {
&--cell {
display: inline-block;
Expand Down
31 changes: 31 additions & 0 deletions frontend/components/icons/Pending.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from "react";
import { COLORS, Colors } from "styles/var/colors";
import { ICON_SIZES, IconSizes } from "styles/var/icon_sizes";

interface IPendingProps {
size?: IconSizes;
color?: Colors;
}

const Pending = ({
size = "medium",
color = "ui-fleet-black-50",
}: IPendingProps) => {
return (
<svg
width={ICON_SIZES[size]}
height={ICON_SIZES[size]}
viewBox="0 0 16 17"
fill="none"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M8 16.5C12.4183 16.5 16 12.9183 16 8.5C16 4.08172 12.4183 0.5 8 0.5C3.58172 0.5 0 4.08172 0 8.5C0 12.9183 3.58172 16.5 8 16.5ZM4.6665 9.5C5.21879 9.5 5.6665 9.05229 5.6665 8.5C5.6665 7.94772 5.21879 7.5 4.6665 7.5C4.11422 7.5 3.6665 7.94772 3.6665 8.5C3.6665 9.05229 4.11422 9.5 4.6665 9.5ZM8.6665 8.5C8.6665 9.05229 8.21879 9.5 7.6665 9.5C7.11422 9.5 6.6665 9.05229 6.6665 8.5C6.6665 7.94772 7.11422 7.5 7.6665 7.5C8.21879 7.5 8.6665 7.94772 8.6665 8.5ZM10.6665 9.5C11.2188 9.5 11.6665 9.05229 11.6665 8.5C11.6665 7.94772 11.2188 7.5 10.6665 7.5C10.1142 7.5 9.6665 7.94772 9.6665 8.5C9.6665 9.05229 10.1142 9.5 10.6665 9.5Z"
fill={COLORS[color]}
/>
</svg>
);
};

export default Pending;
2 changes: 2 additions & 0 deletions frontend/components/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import EmptyTeams from "./EmptyTeams";
import ExternalLink from "./ExternalLink";
import Issue from "./Issue";
import Plus from "./Plus";
import Pending from "./Pending";

import LowDiskSpaceHosts from "./LowDiskSpaceHosts";
import MissingHosts from "./MissingHosts";
Expand Down Expand Up @@ -67,6 +68,7 @@ export const ICON_MAP = {
clipboard: Clipboard,
eye: Eye,
pencil: Pencil,
pending: Pending,
trash: TrashCan,
success: Success,
error: Error,
Expand Down
3 changes: 2 additions & 1 deletion frontend/interfaces/host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import softwareInterface, { ISoftware } from "./software";
import hostQueryResult from "./campaign";
import queryStatsInterface, { IQueryStats } from "./query_stats";
import { ILicense, IDeviceGlobalConfig } from "./config";
import { MdmEnrollmentStatus } from "./mdm";
import { IMacSettings, MdmEnrollmentStatus } from "./mdm";

export default PropTypes.shape({
created_at: PropTypes.string,
Expand Down Expand Up @@ -90,6 +90,7 @@ export interface IHostMdmData {
encryption_key_available: boolean;
enrollment_status: MdmEnrollmentStatus | null;
server_url: string;
profiles?: IMacSettings;
id?: number;
name?: string;
}
Expand Down
13 changes: 13 additions & 0 deletions frontend/interfaces/mdm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,16 @@ export interface IMdmProfile {
export interface IMdmProfilesResponse {
profiles: IMdmProfile[] | null;
}

export type MacMdmProfileStatus = "success" | "pending" | "failed";
export type MacMdmProfileOperationType = "remove" | "install";

export type IHostMacMdmProfile = {
profile_id: number;
name: string;
operation_type: MacMdmProfileOperationType;
status: MacMdmProfileStatus;
detail: string;
};
export type IMacSettings = IHostMacMdmProfile[];
export type MacSettingsStatus = "Failing" | "Latest" | "Pending";
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import PATHS from "router/paths";

import { ISideNavItem } from "../components/SideNav/SideNav";
import Integrations from "./cards/Integrations";
import Mdm from "./cards/Mdm/Mdm";
import Mdm from "./cards/MdmSettings/MdmSettings";

const INTEGRATION_SETTINGS_NAV_ITEMS: ISideNavItem<any>[] = [
// TODO: types
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ interface IABMKeys {
decodedPrivate: string;
}

const baseClass = "mdm-integrations";
const baseClass = "mdm-settings";

const Mdm = (): JSX.Element => {
const { isPremiumTier, config } = useContext(AppContext);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.mdm-integrations {
.mdm-settings {
display: flex;
flex-direction: column;
width: 65%;
Expand All @@ -21,7 +21,7 @@
margin-bottom: 0;
}

.mdm-integrations__edit-team-btn {
.mdm-settings-team-btn {
margin-left: 12px;

.children-wrapper {
Expand Down
2 changes: 1 addition & 1 deletion frontend/pages/hosts/ManageHostsPage/HostTableConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { IUser } from "interfaces/user";
import PATHS from "router/paths";
import permissionUtils from "utilities/permissions";
import { DEFAULT_EMPTY_CELL_VALUE } from "utilities/constants";
import getHostStatusTooltipText from "../helpers";
import { getHostStatusTooltipText } from "../helpers";

interface IGetToggleAllRowsSelectedProps {
checked: boolean;
Expand Down
24 changes: 21 additions & 3 deletions frontend/pages/hosts/details/DeviceUserPage/DeviceUserPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import FleetIcon from "../../../../../assets/images/fleet-avatar-24x24@2x.png";
import PolicyDetailsModal from "../cards/Policies/HostPoliciesTable/PolicyDetailsModal";
import AutoEnrollMdmModal from "./AutoEnrollMdmModal";
import ManualEnrollMdmModal from "./ManualEnrollMdmModal";
import MacSettingsModal from "../MacSettingsModal";

const baseClass = "device-user";

Expand Down Expand Up @@ -75,6 +76,7 @@ const DeviceUserPage = ({
null
);
const [showPolicyDetailsModal, setShowPolicyDetailsModal] = useState(false);
const [showMacSettingsModal, setShowMacSettingsModal] = useState(false);
const [globalConfig, setGlobalConfig] = useState<IDeviceGlobalConfig | null>(
null
);
Expand All @@ -93,7 +95,7 @@ const DeviceUserPage = ({
}
);

const { data: macadmins, refetch: refetchMacadmins } = useQuery(
const { data: deviceMacAdminsData, refetch: refetchMacadmins } = useQuery(
["macadmins", deviceAuthToken],
() => deviceUserAPI.loadHostDetailsExtension(deviceAuthToken, "macadmins"),
{
Expand Down Expand Up @@ -199,6 +201,8 @@ const DeviceUserPage = ({
"percent_disk_space_available",
"gigs_disk_space_available",
"team_name",
"platform",
"mdm",
])
);

Expand Down Expand Up @@ -232,6 +236,11 @@ const DeviceUserPage = ({
},
[showPolicyDetailsModal, setShowPolicyDetailsModal, setSelectedPolicy]
);

const toggleMacSettingsModal = useCallback(() => {
Copy link
Copy Markdown
Contributor

@gillespi314 gillespi314 Feb 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need to use a callback here? Maybe try this instead:

setShowMacSettingsModal((prevState) => !prevState)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was just a pattern I saw elsewhere and followed in lieu of any other input. I'll do that.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gillespi314 I'm actually now remembering, @ghernandez345 doesn't like directly passing set{State} s to other components and prefers a handler of some sort, so this is in line with that. I am curious as to why he prefers that though?

Copy link
Copy Markdown
Contributor

@gillespi314 gillespi314 Feb 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I've seen both approaches and defer to Gabe if his preference is to always define handlers.

In that case you can still avoid the useCallback, I think.

const toggleMacSettingsModal = () => setShowMacSettingsModal((prevState) => !prevState);

Copy link
Copy Markdown
Contributor

@gillespi314 gillespi314 Feb 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useState guarantees that the setter is referentially stable.

React guarantees that setState function identity is stable and won’t change on re-renders. This is why it’s safe to omit from the useEffect or useCallback dependency list.

But the toggleMacSettingsModal handler function will get recreated each render. And I see that it get passed as a prop to child components. This type of set up can result in performance problems when the child component needs intensive calculations to render. https://beta.reactjs.org/reference/react/useCallback#skipping-re-rendering-of-components. So you can add useCallback here to cache this particular handler prop although it might be overkill. Also note that other prop changes can still trigger the child to re-render so this alone isn't a silver bullet for performance problems if the other props aren't being tightly controlled as well. If you do go with useCallback on the handler here, you can use [] for the dependencies if you use the prevState updater function that I suggested.

setShowMacSettingsModal(!showMacSettingsModal);
}, [showMacSettingsModal, setShowMacSettingsModal]);

const onCancelPolicyDetailsModal = useCallback(() => {
setShowPolicyDetailsModal(!showPolicyDetailsModal);
setSelectedPolicy(null);
Expand Down Expand Up @@ -310,10 +319,13 @@ const DeviceUserPage = ({
statusClassName={statusClassName}
titleData={titleData}
diskEncryption={hostDiskEncryption}
isPremiumTier={isPremiumTier}
toggleMacSettingsModal={toggleMacSettingsModal}
hostMacSettings={host?.mdm.profiles}
mdmName={deviceMacAdminsData?.mobile_device_management?.name}
showRefetchSpinner={showRefetchSpinner}
onRefetchHost={onRefetchHost}
renderActionButtons={renderActionButtons}
isPremiumTier={isPremiumTier}
deviceUser
/>
<TabsWrapper>
Expand All @@ -336,7 +348,7 @@ const DeviceUserPage = ({
<AboutCard
aboutData={aboutData}
deviceMapping={deviceMapping}
munki={macadmins?.munki}
munki={deviceMacAdminsData?.munki}
wrapFleetHelper={wrapFleetHelper}
/>
</TabPanel>
Expand Down Expand Up @@ -369,6 +381,12 @@ const DeviceUserPage = ({
policy={selectedPolicy}
/>
)}
{showMacSettingsModal && (
<MacSettingsModal
hostMacSettings={host?.mdm.profiles}
onClose={toggleMacSettingsModal}
/>
)}
</div>
);
};
Expand Down
12 changes: 10 additions & 2 deletions frontend/pages/hosts/details/DeviceUserPage/_styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@
margin-right: $pad-xxlarge;

.info-flex__data {
display: flex;
gap: 4px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
Expand All @@ -101,8 +103,14 @@
vertical-align: sub;
}

.total-issues-count {
margin-left: $pad-small;
.icon {
width: 16px;
height: 16px;
align-self: center;
}

&__text {
padding-left: $pad-xsmall;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import parseOsVersion from "./modals/OSPolicyModal/helpers";

import DiskEncryptionKeyModal from "./modals/DiskEncryptionKeyModal";
import HostActionDropdown from "./HostActionsDropdown/HostActionsDropdown";
import MacSettingsModal from "../MacSettingsModal";

const baseClass = "host-details";

Expand Down Expand Up @@ -127,6 +128,7 @@ const HostDetailsPage = ({
const [showQueryHostModal, setShowQueryHostModal] = useState(false);
const [showPolicyDetailsModal, setPolicyDetailsModal] = useState(false);
const [showOSPolicyModal, setShowOSPolicyModal] = useState(false);
const [showMacSettingsModal, setShowMacSettingsModal] = useState(false);
const [showUnenrollMdmModal, setShowUnenrollMdmModal] = useState(false);
const [showDiskEncryptionModal, setShowDiskEncryptionModal] = useState(false);
const [selectedPolicy, setSelectedPolicy] = useState<IHostPolicy | null>(
Expand All @@ -145,7 +147,6 @@ const HostDetailsPage = ({
] = useState<IHostDiskEncryptionProps>({});
const [usersState, setUsersState] = useState<{ username: string }[]>([]);
const [usersSearchString, setUsersSearchString] = useState("");
const [hideEditMdm, setHideEditMdm] = useState<boolean>(false);

const { data: fleetQueries, error: fleetQueriesError } = useQuery<
IFleetQueriesResponse,
Expand Down Expand Up @@ -388,6 +389,10 @@ const HostDetailsPage = ({
setShowOSPolicyModal(!showOSPolicyModal);
}, [showOSPolicyModal, setShowOSPolicyModal]);

const toggleMacSettingsModal = useCallback(() => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See related comment to DeviceUserPage

setShowMacSettingsModal(!showMacSettingsModal);
}, [showMacSettingsModal, setShowMacSettingsModal]);

const onCancelPolicyDetailsModal = useCallback(() => {
setPolicyDetailsModal(!showPolicyDetailsModal);
setSelectedPolicy(null);
Expand Down Expand Up @@ -620,6 +625,9 @@ const HostDetailsPage = ({
isPremiumTier={isPremiumTier}
isOnlyObserver={isOnlyObserver}
toggleOSPolicyModal={toggleOSPolicyModal}
toggleMacSettingsModal={toggleMacSettingsModal}
hostMacSettings={host?.mdm.profiles}
mdmName={mdm?.name}
showRefetchSpinner={showRefetchSpinner}
onRefetchHost={onRefetchHost}
renderActionButtons={renderActionButtons}
Expand Down Expand Up @@ -737,15 +745,15 @@ const HostDetailsPage = ({
osPolicyLabel={osPolicyLabel}
/>
)}
{showUnenrollMdmModal && !!host && (
<UnenrollMdmModal
hostId={host.id}
onClose={toggleUnenrollMdmModal}
onSuccess={() => {
setHideEditMdm(true);
}}
{showMacSettingsModal && (
<MacSettingsModal
hostMacSettings={host?.mdm.profiles}
onClose={toggleMacSettingsModal}
/>
)}
{showUnenrollMdmModal && !!host && (
<UnenrollMdmModal hostId={host.id} onClose={toggleUnenrollMdmModal} />
)}
{showDiskEncryptionModal && host && (
<DiskEncryptionKeyModal
hostId={host.id}
Expand Down
Loading