Skip to content

Commit

Permalink
🐛 Add task actions to task manager drawer (konveyor#2004)
Browse files Browse the repository at this point in the history
Resolves: konveyor#1972

---------

Signed-off-by: Radoslaw Szwajkowski <[email protected]>
  • Loading branch information
rszwajko authored and sjd78 committed Jul 9, 2024
1 parent 9ca674b commit 87fd225
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 44 deletions.
64 changes: 60 additions & 4 deletions client/src/app/components/task-manager/TaskManagerDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@ import React, { forwardRef, useMemo, useState } from "react";
import { Link } from "react-router-dom";
import dayjs from "dayjs";
import {
Dropdown,
DropdownItem,
DropdownList,
EmptyState,
EmptyStateActions,
EmptyStateBody,
EmptyStateFooter,
EmptyStateHeader,
EmptyStateIcon,
EmptyStateVariant,
MenuToggle,
MenuToggleElement,
NotificationDrawer,
NotificationDrawerBody,
NotificationDrawerHeader,
Expand All @@ -18,15 +23,16 @@ import {
NotificationDrawerListItemHeader,
Tooltip,
} from "@patternfly/react-core";
import { CubesIcon } from "@patternfly/react-icons";
import { CubesIcon, EllipsisVIcon } from "@patternfly/react-icons";
import { css } from "@patternfly/react-styles";

import { TaskState } from "@app/api/models";
import { Task, TaskState } from "@app/api/models";
import { useTaskManagerContext } from "./TaskManagerContext";
import { useServerTasks } from "@app/queries/tasks";

import "./TaskManagerDrawer.css";
import { TaskStateIcon } from "../Icons";
import { useTaskActions } from "@app/pages/tasks/useTaskActions";

/** A version of `Task` specific for the task manager drawer components */
interface TaskManagerTask {
Expand All @@ -47,6 +53,9 @@ interface TaskManagerTask {
applicationId: number;
applicationName: string;
preemptEnabled: boolean;

// full object to be used with library functions
_: Task;
}

const PAGE_SIZE = 20;
Expand All @@ -61,6 +70,9 @@ export const TaskManagerDrawer: React.FC<TaskManagerDrawerProps> = forwardRef(
const { tasks } = useTaskManagerData();

const [expandedItems, setExpandedItems] = useState<number[]>([]);
const [taskWithExpandedActions, setTaskWithExpandedAction] = useState<
number | boolean
>(false);

const closeDrawer = () => {
setIsExpanded(!isExpanded);
Expand Down Expand Up @@ -109,6 +121,10 @@ export const TaskManagerDrawer: React.FC<TaskManagerDrawerProps> = forwardRef(
: expandedItems.filter((i) => i !== task.id)
);
}}
actionsExpanded={task.id === taskWithExpandedActions}
onActionsExpandToggle={(flag: boolean) =>
setTaskWithExpandedAction(flag && task.id)
}
/>
))}
</NotificationDrawerList>
Expand All @@ -130,13 +146,22 @@ const TaskItem: React.FC<{
task: TaskManagerTask;
expanded: boolean;
onExpandToggle: (expand: boolean) => void;
}> = ({ task, expanded, onExpandToggle }) => {
actionsExpanded: boolean;
onActionsExpandToggle: (expand: boolean) => void;
}> = ({
task,
expanded,
onExpandToggle,
actionsExpanded,
onActionsExpandToggle,
}) => {
const starttime = dayjs(task.started ?? task.createTime);
const title = expanded
? `${task.id} (${task.addon})`
: `${task.id} (${task.addon}) - ${task.applicationName} - ${
task.priority ?? 0
}`;
const taskActionItems = useTaskActions(task._);

return (
<NotificationDrawerListItem
Expand All @@ -153,7 +178,36 @@ const TaskItem: React.FC<{
title={title}
icon={<TaskStateToIcon taskState={task.state} />}
>
{/* Put the item's action menu here */}
<Dropdown
onSelect={() => onActionsExpandToggle(false)}
isOpen={actionsExpanded}
onOpenChange={() => onActionsExpandToggle(false)}
popperProps={{ position: "right" }}
toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
<MenuToggle
ref={toggleRef}
isExpanded={actionsExpanded}
isDisabled={taskActionItems.every(({ isDisabled }) => isDisabled)}
onClick={() => onActionsExpandToggle(!actionsExpanded)}
variant="plain"
aria-label={`Actions for task ${task.name}`}
>
<EllipsisVIcon aria-hidden="true" />
</MenuToggle>
)}
>
<DropdownList>
{taskActionItems.map(({ title, onClick, isDisabled }) => (
<DropdownItem
key={title}
onClick={onClick}
isDisabled={isDisabled}
>
{title}
</DropdownItem>
))}
</DropdownList>
</Dropdown>
</NotificationDrawerListItemHeader>
{expanded ? (
<NotificationDrawerListItemBody
Expand Down Expand Up @@ -217,6 +271,8 @@ const useTaskManagerData = () => {
applicationName: task.application.name,
preemptEnabled: task?.policy?.preemptEnabled ?? false,

_: task,

// TODO: Add any checks that could be needed later...
// - isCancelable (does the current user own the task? other things to check?)
// - isPreemptionToggleAllowed
Expand Down
10 changes: 10 additions & 0 deletions client/src/app/pages/tasks/TaskActionColumn.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React, { FC } from "react";

import { Task } from "@app/api/models";
import { ActionsColumn } from "@patternfly/react-table";
import { useTaskActions } from "./useTaskActions";

export const TaskActionColumn: FC<{ task: Task }> = ({ task }) => {
const actions = useTaskActions(task);
return <ActionsColumn items={actions} />;
};
41 changes: 3 additions & 38 deletions client/src/app/pages/tasks/tasks-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,7 @@ import {
ToolbarContent,
ToolbarItem,
} from "@patternfly/react-core";
import {
Table,
Tbody,
Th,
Thead,
Tr,
Td,
ActionsColumn,
} from "@patternfly/react-table";
import { Table, Tbody, Th, Thead, Tr, Td } from "@patternfly/react-table";
import { CubesIcon } from "@patternfly/react-icons";

import { FilterToolbar, FilterType } from "@app/components/FilterToolbar";
Expand All @@ -46,9 +38,9 @@ import { Task } from "@app/api/models";
import { IconWithLabel, TaskStateIcon } from "@app/components/Icons";
import { ManageColumnsToolbar } from "../applications/applications-table/components/manage-columns-toolbar";
import dayjs from "dayjs";
import { useTaskActions } from "./useTaskActions";
import { formatPath } from "@app/utils/utils";
import { Paths } from "@app/Paths";
import { TaskActionColumn } from "./TaskActionColumn";

export const TasksPage: React.FC = () => {
const { t } = useTranslation();
Expand Down Expand Up @@ -205,9 +197,6 @@ export const TasksPage: React.FC = () => {
filterToolbarProps.setFilterValues({});
};

const { cancelTask, togglePreemption, canCancel, canTogglePreemption } =
useTaskActions();

const toCells = ({
id,
application,
Expand Down Expand Up @@ -335,31 +324,7 @@ export const TasksPage: React.FC = () => {
isActionCell
id={`row-actions-${task.id}`}
>
<ActionsColumn
items={[
{
title: t("actions.cancel"),
isDisabled: !canCancel(task.state),
onClick: () => cancelTask(task.id),
},
{
title: task.policy?.preemptEnabled
? t("actions.disablePreemption")
: t("actions.enablePreemption"),
isDisabled: !canTogglePreemption(task.state),
onClick: () => togglePreemption(task),
},
{
title: t("actions.taskDetails"),
onClick: () =>
history.push(
formatPath(Paths.taskDetails, {
taskId: task.id,
})
),
},
]}
/>
<TaskActionColumn task={task} />
</Td>
</TableRowContentWithControls>
</Tr>
Expand Down
37 changes: 35 additions & 2 deletions client/src/app/pages/tasks/useTaskActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@ import {
import { Task, TaskState } from "@app/api/models";
import { NotificationsContext } from "@app/components/NotificationsContext";
import { useTranslation } from "react-i18next";
import { useHistory } from "react-router-dom";
import { formatPath } from "@app/utils/utils";
import { Paths } from "@app/Paths";

const canCancel = (state: TaskState = "No task") =>
!["Succeeded", "Failed", "Canceled"].includes(state);

const canTogglePreemption = (state: TaskState = "No task") =>
!["Succeeded", "Failed", "Canceled", "Running"].includes(state);

export const useTaskActions = () => {
const useAsyncTaskActions = () => {
const { t } = useTranslation();
const { pushNotification } = React.useContext(NotificationsContext);
const { mutate: cancelTask } = useCancelTaskMutation(
Expand Down Expand Up @@ -55,5 +58,35 @@ export const useTaskActions = () => {
},
});

return { cancelTask, togglePreemption, canCancel, canTogglePreemption };
return { cancelTask, togglePreemption };
};

export const useTaskActions = (task: Task) => {
const { cancelTask, togglePreemption } = useAsyncTaskActions();
const { t } = useTranslation();
const history = useHistory();

return [
{
title: t("actions.cancel"),
isDisabled: !canCancel(task.state),
onClick: () => cancelTask(task.id),
},
{
title: task.policy?.preemptEnabled
? t("actions.disablePreemption")
: t("actions.enablePreemption"),
isDisabled: !canTogglePreemption(task.state),
onClick: () => togglePreemption(task),
},
{
title: t("actions.taskDetails"),
onClick: () =>
history.push(
formatPath(Paths.taskDetails, {
taskId: task.id,
})
),
},
];
};

0 comments on commit 87fd225

Please sign in to comment.