mirror of
https://github.com/fleetdm/fleet
synced 2026-05-23 08:58:41 +00:00
Fix UI cancel error message lock wipe (#28071)
For #28018 some fixes for the UI of cancel activities including: - showing proper error message - correct permission checking for allowing users to cancel activity - refresh all host details after lock or wipe command to get updated host status.
This commit is contained in:
parent
2d45272bc8
commit
1f9244ef8e
6 changed files with 39 additions and 18 deletions
|
|
@ -24,7 +24,7 @@ export const isValidPemCertificate = (cert: string): boolean => {
|
|||
return regexPemHeader.test(cert) && regexPemFooter.test(cert);
|
||||
};
|
||||
|
||||
const hasStatusKey = (value: unknown): value is { status: number } => {
|
||||
export const hasStatusKey = (value: unknown): value is { status: number } => {
|
||||
return (
|
||||
typeof value === "object" &&
|
||||
value !== null &&
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ import { IHostPolicy } from "interfaces/policy";
|
|||
import { IQueryStats } from "interfaces/query_stats";
|
||||
import { IHostSoftware } from "interfaces/software";
|
||||
import { ITeam } from "interfaces/team";
|
||||
import { IHostUpcomingActivity } from "interfaces/activity";
|
||||
import { ActivityType, IHostUpcomingActivity } from "interfaces/activity";
|
||||
import {
|
||||
IHostCertificate,
|
||||
CERTIFICATES_DEFAULT_SORT,
|
||||
|
|
@ -163,6 +163,7 @@ const HostDetailsPage = ({
|
|||
config,
|
||||
currentUser,
|
||||
isGlobalAdmin = false,
|
||||
isGlobalMaintainer,
|
||||
isGlobalObserver,
|
||||
isPremiumTier = false,
|
||||
isOnlyObserver,
|
||||
|
|
@ -836,6 +837,12 @@ const HostDetailsPage = ({
|
|||
router.push(navPath);
|
||||
};
|
||||
|
||||
const isHostTeamAdmin = permissions.isTeamAdmin(currentUser, host?.team_id);
|
||||
const isHostTeamMaintainer = permissions.isTeamMaintainer(
|
||||
currentUser,
|
||||
host?.team_id
|
||||
);
|
||||
|
||||
/* Context team id might be different that host's team id
|
||||
Observer plus must be checked against host's team id */
|
||||
const isGlobalOrHostsTeamObserverPlus =
|
||||
|
|
@ -969,6 +976,12 @@ const HostDetailsPage = ({
|
|||
? pastActivitiesIsError
|
||||
: upcomingActivitiesIsError
|
||||
}
|
||||
canCancelActivities={
|
||||
isGlobalAdmin ||
|
||||
isGlobalMaintainer ||
|
||||
isHostTeamAdmin ||
|
||||
isHostTeamMaintainer
|
||||
}
|
||||
upcomingCount={upcomingActivities?.count || 0}
|
||||
onChangeTab={onChangeActivityTab}
|
||||
onNextPage={() => setActivityPage(activityPage + 1)}
|
||||
|
|
@ -1204,6 +1217,16 @@ const HostDetailsPage = ({
|
|||
hostId={host.id}
|
||||
activity={selectedCancelActivity}
|
||||
onCancelActivity={() => refetchUpcomingActivities()}
|
||||
onSuccessCancel={(activity) => {
|
||||
// only for windows and linux hosts we want to refetch host details
|
||||
if (
|
||||
(activity.type === ActivityType.RanScript &&
|
||||
host.platform === "windows") ||
|
||||
host.platform === "linux"
|
||||
) {
|
||||
refetchHostDetails();
|
||||
}
|
||||
}}
|
||||
onExit={() => setSelectedCancelActivity(null)}
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ const baseClass = "cancel-activity-modal";
|
|||
interface ICancelActivityModalProps {
|
||||
hostId: number;
|
||||
activity: IHostUpcomingActivity;
|
||||
onCancelActivity: () => void;
|
||||
onCancelActivity: (activity: IHostUpcomingActivity) => void;
|
||||
onSuccessCancel: (activity: IHostUpcomingActivity) => void;
|
||||
onExit: () => void;
|
||||
}
|
||||
|
||||
|
|
@ -25,6 +26,7 @@ const CancelActivityModal = ({
|
|||
hostId,
|
||||
activity,
|
||||
onCancelActivity,
|
||||
onSuccessCancel,
|
||||
onExit,
|
||||
}: ICancelActivityModalProps) => {
|
||||
const { renderFlash } = useContext(NotificationContext);
|
||||
|
|
@ -37,10 +39,11 @@ const CancelActivityModal = ({
|
|||
try {
|
||||
await activitiesAPI.cancelHostActivity(hostId, activity.uuid);
|
||||
renderFlash("success", "Activity successfully canceled.");
|
||||
onSuccessCancel(activity);
|
||||
} catch (err) {
|
||||
renderFlash("error", getErrorMessage(err));
|
||||
}
|
||||
onCancelActivity();
|
||||
onCancelActivity(activity);
|
||||
onExit();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { isAxiosError } from "axios";
|
||||
import { hasStatusKey } from "pages/hosts/ManageHostsPage/helpers";
|
||||
|
||||
const DEFAULT_ERR_MESSAGE = "Couldn't cancel activity. Please try again.";
|
||||
const LOCK_WIPE_ERR_MESSAGE =
|
||||
|
|
@ -8,7 +8,7 @@ const ACTIVITY_ALREADY_HAPPENED_ERR_MESSAGE =
|
|||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const getErrorMessage = (err: unknown) => {
|
||||
if (isAxiosError(err)) {
|
||||
if (hasStatusKey(err)) {
|
||||
if (err.status === 404) return ACTIVITY_ALREADY_HAPPENED_ERR_MESSAGE;
|
||||
|
||||
// display server error message if error is 400
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ interface IActivityProps {
|
|||
isError?: boolean;
|
||||
className?: string;
|
||||
upcomingCount: number;
|
||||
canCancelActivities: boolean;
|
||||
onChangeTab: (index: number, last: number, event: Event) => void;
|
||||
onNextPage: () => void;
|
||||
onPreviousPage: () => void;
|
||||
|
|
@ -53,6 +54,7 @@ const Activity = ({
|
|||
isError,
|
||||
className,
|
||||
upcomingCount,
|
||||
canCancelActivities,
|
||||
onChangeTab,
|
||||
onNextPage,
|
||||
onPreviousPage,
|
||||
|
|
@ -107,6 +109,7 @@ const Activity = ({
|
|||
isError={isError}
|
||||
onNextPage={onNextPage}
|
||||
onPreviousPage={onPreviousPage}
|
||||
canCancelActivities={canCancelActivities}
|
||||
/>
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import React, { useContext } from "react";
|
||||
import React from "react";
|
||||
|
||||
import { IHostUpcomingActivity } from "interfaces/activity";
|
||||
import { IHostUpcomingActivitiesResponse } from "services/entities/activities";
|
||||
import { AppContext } from "context/app";
|
||||
|
||||
import DataError from "components/DataError";
|
||||
import Pagination from "components/Pagination";
|
||||
|
|
@ -16,6 +15,7 @@ const baseClass = "upcoming-activity-feed";
|
|||
interface IUpcomingActivityFeedProps {
|
||||
activities?: IHostUpcomingActivitiesResponse;
|
||||
isError?: boolean;
|
||||
canCancelActivities: boolean;
|
||||
onShowDetails: ShowActivityDetailsHandler;
|
||||
onCancel: (activity: IHostUpcomingActivity) => void;
|
||||
onNextPage: () => void;
|
||||
|
|
@ -25,17 +25,12 @@ interface IUpcomingActivityFeedProps {
|
|||
const UpcomingActivityFeed = ({
|
||||
activities,
|
||||
isError = false,
|
||||
canCancelActivities,
|
||||
onShowDetails,
|
||||
onCancel,
|
||||
onNextPage,
|
||||
onPreviousPage,
|
||||
}: IUpcomingActivityFeedProps) => {
|
||||
const {
|
||||
isTeamMaintainerOrTeamAdmin,
|
||||
isGlobalAdmin,
|
||||
isGlobalMaintainer,
|
||||
} = useContext(AppContext);
|
||||
|
||||
if (isError) {
|
||||
return <DataError />;
|
||||
}
|
||||
|
|
@ -56,9 +51,6 @@ const UpcomingActivityFeed = ({
|
|||
);
|
||||
}
|
||||
|
||||
const canCancel =
|
||||
isGlobalAdmin || isGlobalMaintainer || isTeamMaintainerOrTeamAdmin;
|
||||
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<div className={`${baseClass}__feed-list`}>
|
||||
|
|
@ -71,7 +63,7 @@ const UpcomingActivityFeed = ({
|
|||
tab="upcoming"
|
||||
activity={activity}
|
||||
onShowDetails={onShowDetails}
|
||||
hideCancel={!canCancel}
|
||||
hideCancel={!canCancelActivities}
|
||||
onCancel={() => onCancel(activity)}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in a new issue