diff --git a/client/src/app/Paths.ts b/client/src/app/Paths.ts index 8bd2e6f1d0..46a41ba5e8 100644 --- a/client/src/app/Paths.ts +++ b/client/src/app/Paths.ts @@ -3,6 +3,7 @@ export const DevPaths = { applications: "/applications", applicationsAnalysisDetails: "/applications/:applicationId/analysis-details/:taskId", + applicationsTaskDetails: "/applications/:applicationId/tasks/:taskId", applicationsAnalysisDetailsAttachment: "/applications/:applicationId/analysis-details/:taskId/attachments/:attachmentId", applicationsAnalysisTab: "/applications/analysis-tab", @@ -108,4 +109,5 @@ export interface AnalysisDetailsAttachmentRoute { export interface TaskDetailsAttachmentRoute { taskId: string; attachmentId: string; + applicationId: string; } diff --git a/client/src/app/Routes.tsx b/client/src/app/Routes.tsx index 64cb995cc2..f1e7796be3 100644 --- a/client/src/app/Routes.tsx +++ b/client/src/app/Routes.tsx @@ -85,6 +85,11 @@ export const devRoutes: IRoute[] = [ comp: AnalysisDetails, exact: true, }, + { + path: Paths.applicationsTaskDetails, + comp: TaskDetails, + exact: true, + }, { path: Paths.applicationsAnalysisDetailsAttachment, comp: AnalysisDetails, diff --git a/client/src/app/components/task-manager/TaskManagerDrawer.tsx b/client/src/app/components/task-manager/TaskManagerDrawer.tsx index 10824bc16e..8e9141a4be 100644 --- a/client/src/app/components/task-manager/TaskManagerDrawer.tsx +++ b/client/src/app/components/task-manager/TaskManagerDrawer.tsx @@ -163,6 +163,7 @@ const TaskItem: React.FC<{ : `${task.id} (${task.addon}) - ${task.applicationName} - ${ task.priority ?? 0 }`; + const taskActionItems = useTaskActions(task._); return ( diff --git a/client/src/app/pages/applications/components/application-detail-drawer/application-detail-drawer.tsx b/client/src/app/pages/applications/components/application-detail-drawer/application-detail-drawer.tsx index 882578b58e..bceb567576 100644 --- a/client/src/app/pages/applications/components/application-detail-drawer/application-detail-drawer.tsx +++ b/client/src/app/pages/applications/components/application-detail-drawer/application-detail-drawer.tsx @@ -65,6 +65,29 @@ import { useFetchArchetypes } from "@app/queries/archetypes"; import { useFetchAssessments } from "@app/queries/assessments"; import { DecoratedApplication } from "../../applications-table/useDecoratedApplications"; import { TaskStates } from "@app/queries/tasks"; +import { Toolbar, ToolbarContent, ToolbarItem } from "@patternfly/react-core"; +import { Table, Tbody, Td, Th, Thead, Tr } from "@patternfly/react-table"; +import { + useTableControlState, + useTableControlProps, +} from "@app/hooks/table-controls"; +import { SimplePagination } from "@app/components/SimplePagination"; +import { useServerTasks } from "@app/queries/tasks"; +import { FilterToolbar, FilterType } from "@app/components/FilterToolbar"; +import { + getHubRequestParams, + deserializeFilterUrlParams, +} from "@app/hooks/table-controls"; +import { useSelectionState } from "@migtools/lib-ui"; +import { TablePersistenceKeyPrefix } from "@app/Constants"; +import { TaskActionColumn } from "../../../../pages/tasks/TaskActionColumn"; +import { + ConditionalTableBody, + TableHeaderContentWithControls, + TableRowContentWithControls, +} from "@app/components/TableControls"; +import { IconWithLabel, TaskStateIcon } from "@app/components/Icons"; +import { taskStateToLabel } from "@app/pages/tasks/tasks-page"; export interface IApplicationDetailDrawerProps extends Pick { @@ -73,18 +96,20 @@ export interface IApplicationDetailDrawerProps onEditClick: () => void; } -enum TabKey { +export enum TabKey { Details = 0, Tags, Reports, Facts, Reviews, + Tasks, } export const ApplicationDetailDrawer: React.FC< IApplicationDetailDrawerProps > = ({ application, task, onCloseClick, onEditClick }) => { const { t } = useTranslation(); + const [activeTabKey, setActiveTabKey] = React.useState( TabKey.Details ); @@ -152,6 +177,14 @@ export const ApplicationDetailDrawer: React.FC< )} + {!application ? null : ( + {t("terms.tasks")}} + > + + + )} @@ -521,3 +554,206 @@ const TabReportsContent: React.FC<{ ); }; + +const TabTasksContent: React.FC<{ + application: DecoratedApplication; + task: TaskDashboard | null; +}> = ({ application, task }) => { + const { t } = useTranslation(); + const history = useHistory(); + const urlParams = new URLSearchParams(window.location.search); + const filters = urlParams.get("filters"); + const deserializedFilterValues = deserializeFilterUrlParams({ filters }); + const tableControlState = useTableControlState({ + tableName: "tasks-apps-table", + persistTo: "urlParams", + persistenceKeyPrefix: TablePersistenceKeyPrefix.tasks, + columnNames: { + taskId: "Task ID", + taskKind: "Task Kind", + status: "Status", + }, + isFilterEnabled: true, + isSortEnabled: true, + isPaginationEnabled: true, + sortableColumns: ["taskId", "taskKind", "status"], + initialSort: { columnKey: "taskId", direction: "asc" }, + initialFilterValues: deserializedFilterValues, + filterCategories: [ + { + categoryKey: "id", + title: "ID", + type: FilterType.numsearch, + placeholderText: t("actions.filterBy", { + what: "ID...", + }), + getServerFilterValue: (value) => { + console.log("this id:", value); + return value ? value : []; + }, + }, + { + categoryKey: "kind", + title: t("terms.kind"), + type: FilterType.search, + placeholderText: t("actions.filterBy", { + what: t("terms.kind") + "...", + }), + getServerFilterValue: (value) => (value ? [`*${value[0]}*`] : []), + }, + { + categoryKey: "state", + title: t("terms.status"), + type: FilterType.search, + placeholderText: t("actions.filterBy", { + what: t("terms.status") + "...", + }), + getServerFilterValue: (value) => (value ? [`*${value[0]}*`] : []), + }, + ], + initialItemsPerPage: 10, + }); + + const { + result: { data: currentPageItems = [], total: totalItemCount }, + isFetching, + fetchError, + } = useServerTasks( + getHubRequestParams({ + ...tableControlState, + hubSortFieldKeys: { + taskId: "id", + taskKind: "kind", + status: "status", + }, + implicitFilters: [ + { + field: "application.id", + operator: "=", + value: application.id, + }, + ], + }), + 5000 + ); + const tableControls = useTableControlProps({ + ...tableControlState, + idProperty: "id", + currentPageItems: currentPageItems, + totalItemCount, + isLoading: isFetching, + variant: "compact", + selectionState: useSelectionState({ + items: currentPageItems, + isEqual: (a, b) => a.name === b.name, + }), + }); + + const { + numRenderedColumns, + propHelpers: { + toolbarProps, + filterToolbarProps, + paginationToolbarItemProps, + paginationProps, + tableProps, + getThProps, + getTrProps, + getTdProps, + }, + } = tableControls; + + const clearFilters = () => { + const currentPath = history.location.pathname; + const newSearch = new URLSearchParams(history.location.search); + newSearch.delete("filters"); + history.push(`${currentPath}`); + }; + return ( + <> + + + + + + + + + + + + + + + + + + {currentPageItems?.map((task, rowIndex) => ( + + + + + + + + + ))} + + +
+ + + +
{task.id} + {task.kind} + + } + label={ + + {t(taskStateToLabel[task.state ?? "No task"])} + + } + /> + + +
+ + + ); +}; diff --git a/client/src/app/pages/tasks/TaskDetails.tsx b/client/src/app/pages/tasks/TaskDetails.tsx index 86381a285c..31770baed5 100644 --- a/client/src/app/pages/tasks/TaskDetails.tsx +++ b/client/src/app/pages/tasks/TaskDetails.tsx @@ -6,23 +6,43 @@ import { Paths, TaskDetailsAttachmentRoute } from "@app/Paths"; import "@app/components/simple-document-viewer/SimpleDocumentViewer.css"; import { formatPath } from "@app/utils/utils"; import { TaskDetailsBase } from "./TaskDetailsBase"; +import { useFetchApplicationById } from "@app/queries/applications"; export const TaskDetails = () => { const { t } = useTranslation(); - const { taskId, attachmentId } = useParams(); - const detailsPath = formatPath(Paths.taskDetails, { taskId }); + const { taskId, attachmentId, applicationId } = + useParams(); + const currentPath = window.location.pathname; + const isFromApplication = currentPath.includes("application") ? true : false; + const { application } = useFetchApplicationById(applicationId); + + const appName: string = application?.name ?? t("terms.unknown"); + console.log(appName); + const detailsPath = isFromApplication + ? formatPath(Paths.applicationsTaskDetails, { + applicationId: applicationId, + taskId: taskId, + }) + : formatPath(Paths.taskDetails, { taskId }); + return ( `Task details for task ${taskId}, ${taskName}`} formatAttachmentPath={(attachmentId) => @@ -36,5 +56,4 @@ export const TaskDetails = () => { /> ); }; - export default TaskDetails; diff --git a/client/src/app/pages/tasks/tasks-page.tsx b/client/src/app/pages/tasks/tasks-page.tsx index d4452dbb52..2b6cd1b0ec 100644 --- a/client/src/app/pages/tasks/tasks-page.tsx +++ b/client/src/app/pages/tasks/tasks-page.tsx @@ -50,7 +50,7 @@ import { formatPath } from "@app/utils/utils"; import { Paths } from "@app/Paths"; import { TaskActionColumn } from "./TaskActionColumn"; -const taskStateToLabel: Record = { +export const taskStateToLabel: Record = { "No task": "taskState.NoTask", "not supported": "", Canceled: "taskState.Canceled", @@ -71,7 +71,6 @@ export const TasksPage: React.FC = () => { const urlParams = new URLSearchParams(window.location.search); const filters = urlParams.get("filters") ?? ""; - const deserializedFilterValues = deserializeFilterUrlParams({ filters }); const tableControlState = useTableControlState({ diff --git a/client/src/app/pages/tasks/useTaskActions.tsx b/client/src/app/pages/tasks/useTaskActions.tsx index bfcd70c288..ee3910e54d 100644 --- a/client/src/app/pages/tasks/useTaskActions.tsx +++ b/client/src/app/pages/tasks/useTaskActions.tsx @@ -81,12 +81,23 @@ export const useTaskActions = (task: Task) => { }, { title: t("actions.taskDetails"), - onClick: () => - history.push( - formatPath(Paths.taskDetails, { - taskId: task.id, - }) - ), + onClick: () => { + const currentPath = window.location.pathname; + if (currentPath.includes("application")) { + history.push( + formatPath(Paths.applicationsTaskDetails, { + applicationId: task.application?.id, + taskId: task?.id, + }) + ); + } else { + history.push( + formatPath(Paths.taskDetails, { + taskId: task?.id, + }) + ); + } + }, }, ]; };