From ec341164d5bdef41de765b23b2e29b1484dee080 Mon Sep 17 00:00:00 2001 From: Aaron Liu Date: Sun, 18 May 2025 11:31:10 +0800 Subject: [PATCH] feat(dashboard): admin can now view task detailed steps --- src/api/dashboard.ts | 1 + src/component/Admin/Task/TaskContent.tsx | 2 +- .../Admin/Task/TaskDialog/TaskForm.tsx | 19 ++- .../Pages/Tasks/StepProgressPopover.tsx | 10 +- src/component/Pages/Tasks/TaskDetail.tsx | 8 +- src/component/Pages/Tasks/TaskProgress.tsx | 42 ++++--- .../Pages/Tasks/TaskProgressStep.tsx | 119 +++++++----------- 7 files changed, 97 insertions(+), 104 deletions(-) diff --git a/src/api/dashboard.ts b/src/api/dashboard.ts index 2286965..70b2be0 100644 --- a/src/api/dashboard.ts +++ b/src/api/dashboard.ts @@ -510,6 +510,7 @@ export interface Task extends CommonMixin { }; user_hash_id?: string; + task_hash_id?: string; summary?: TaskSummary; node?: Node; } diff --git a/src/component/Admin/Task/TaskContent.tsx b/src/component/Admin/Task/TaskContent.tsx index a7fede3..c9485a1 100644 --- a/src/component/Admin/Task/TaskContent.tsx +++ b/src/component/Admin/Task/TaskContent.tsx @@ -6,7 +6,7 @@ import { TaskSummary, TaskType } from "../../../api/workflow"; import CrUri, { Filesystem } from "../../../util/uri"; import TaskSummaryTitle from "../../Pages/Tasks/TaskSummaryTitle"; -const userTaskTypes: string[] = [ +export const userTaskTypes: string[] = [ TaskType.relocate, TaskType.create_archive, TaskType.extract_archive, diff --git a/src/component/Admin/Task/TaskDialog/TaskForm.tsx b/src/component/Admin/Task/TaskDialog/TaskForm.tsx index dc86b61..b4a2913 100644 --- a/src/component/Admin/Task/TaskDialog/TaskForm.tsx +++ b/src/component/Admin/Task/TaskDialog/TaskForm.tsx @@ -29,9 +29,10 @@ import UserAvatar from "../../../Common/User/UserAvatar"; import FileBadge from "../../../FileManager/FileBadge"; import SettingForm from "../../../Pages/Setting/SettingForm"; import DownloadFileList from "../../../Pages/Tasks/DownloadFileList"; +import TaskProgress from "../../../Pages/Tasks/TaskProgress"; import { getTaskStatusText } from "../../../Pages/Tasks/TaskProps"; import UserDialog from "../../User/UserDialog/UserDialog"; -import { processTaskContent } from "../TaskContent"; +import { processTaskContent, userTaskTypes } from "../TaskContent"; import BlobErrors from "./BlobErrors"; dayjs.extend(duration); @@ -66,6 +67,10 @@ const TaskForm = ({ values }: { values: Task }) => { return res; }, [values]); + const isUserTask = useMemo(() => { + return userTaskTypes.includes(values.type ?? ""); + }, [values]); + return ( <> setUserDialogOpen(false)} userID={userDialogID} /> @@ -225,6 +230,18 @@ const TaskForm = ({ values }: { values: Task }) => { )} + {isUserTask && ( + + + + )} + {values?.public_state?.error_history && ( diff --git a/src/component/Pages/Tasks/StepProgressPopover.tsx b/src/component/Pages/Tasks/StepProgressPopover.tsx index c541576..5359a17 100644 --- a/src/component/Pages/Tasks/StepProgressPopover.tsx +++ b/src/component/Pages/Tasks/StepProgressPopover.tsx @@ -3,13 +3,13 @@ import HoverPopover from "material-ui-popup-state/HoverPopover"; import { useCallback, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { getTasksPhaseProgress } from "../../../api/api.ts"; -import { TaskProgress, TaskProgresses, TaskResponse } from "../../../api/workflow.ts"; +import { TaskProgress, TaskProgresses } from "../../../api/workflow.ts"; import { useAppDispatch } from "../../../redux/hooks.ts"; import { sizeToString } from "../../../util"; import StepProgressBar from "./StepProgressBar.tsx"; export interface StepProgressPopoverProps extends PopoverProps { - task: TaskResponse; + taskId: string; } export const ProgressKeys = { @@ -167,7 +167,7 @@ const ProgressBar = ({ pkey, p }: { pkey: string; p: TaskProgress }) => { } }; -const StepProgressPopover = ({ task, open, ...rest }: StepProgressPopoverProps) => { +const StepProgressPopover = ({ taskId, open, ...rest }: StepProgressPopoverProps) => { const { t } = useTranslation(); const dispatch = useAppDispatch(); const stopPropagation = useCallback((e: any) => e.stopPropagation(), []); @@ -175,9 +175,9 @@ const StepProgressPopover = ({ task, open, ...rest }: StepProgressPopoverProps) useEffect(() => { if (open) { - dispatch(getTasksPhaseProgress(task.id)).then((res) => setProgress(res)); + dispatch(getTasksPhaseProgress(taskId)).then((res) => setProgress(res)); } - }, [open, task]); + }, [open, taskId]); return ( { )} {task.status == TaskStatus.error && {task.error}} - + diff --git a/src/component/Pages/Tasks/TaskProgress.tsx b/src/component/Pages/Tasks/TaskProgress.tsx index b1bda52..ab591bc 100644 --- a/src/component/Pages/Tasks/TaskProgress.tsx +++ b/src/component/Pages/Tasks/TaskProgress.tsx @@ -1,12 +1,18 @@ import { Box, Stepper, useMediaQuery, useTheme } from "@mui/material"; import { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; -import { NodeTypes, TaskResponse, TaskStatus, TaskType } from "../../../api/workflow.ts"; +import { NodeTypes, TaskStatus, TaskSummary, TaskType } from "../../../api/workflow.ts"; import PieceProgress from "./PieceProgress.tsx"; import TaskProgressStep from "./TaskProgressStep.tsx"; export interface TaskProgressProps { - task: TaskResponse; + taskId: string; + taskStatus: string; + taskType: string; + summary?: TaskSummary; + node?: { + type: string; + }; } interface StepModel { @@ -191,56 +197,52 @@ const stepOptions: { ], }; -const TaskProgress = ({ task }: TaskProgressProps) => { +const TaskProgress = ({ taskId, taskStatus, taskType, summary, node }: TaskProgressProps) => { const { t } = useTranslation(); const [activeStep, setActiveStep] = useState(0); const steps = useMemo((): StepModel[] => { - return stepOptions[task.type]?.[task.node?.type == NodeTypes.slave ? 1 : 0] ?? []; - }, [task.id, task.node?.type]); + return stepOptions[taskType]?.[node?.type == NodeTypes.slave ? 1 : 0] ?? []; + }, [taskId, node?.type]); const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down("md")); useEffect(() => { - if (task.status == TaskStatus.queued) { + if (taskStatus == TaskStatus.queued) { setActiveStep(0); return; } - if (task.status == TaskStatus.completed) { + if (taskStatus == TaskStatus.completed) { setActiveStep(steps.length); return; } let active = 1; for (let i = 1; i < steps.length; i++) { - if (steps[i].state == task.summary?.phase) { + if (steps[i].state == summary?.phase) { active = i; } } setActiveStep(active); - }, [steps, task]); + }, [steps, taskStatus, summary?.phase]); return ( {steps.map((step, index) => ( ))} - {task.type == TaskType.remote_download && - task.summary?.props.download?.pieces && - task.summary?.phase == "monitor" && ( - - )} + {taskType == TaskType.remote_download && summary?.props.download?.pieces && summary?.phase == "monitor" && ( + + )} ); }; diff --git a/src/component/Pages/Tasks/TaskProgressStep.tsx b/src/component/Pages/Tasks/TaskProgressStep.tsx index 17c6583..e5f43ac 100644 --- a/src/component/Pages/Tasks/TaskProgressStep.tsx +++ b/src/component/Pages/Tasks/TaskProgressStep.tsx @@ -1,76 +1,51 @@ -import { TaskResponse, TaskStatus } from "../../../api/workflow.ts"; -import { - Step, - StepIcon, - StepIconProps, - StepLabel, - Typography, -} from "@mui/material"; +import { Step, StepIcon, StepIconProps, StepLabel, Typography } from "@mui/material"; import { StepProps } from "@mui/material/Step/Step"; -import { useTranslation } from "react-i18next"; -import FacebookCircularProgress from "../../Common/CircularProgress.tsx"; -import DismissCircleFilled from "../../Icons/DismissCircleFilled.tsx"; -import { useMemo } from "react"; -import CheckCircleFilled from "../../Icons/CheckCircleFilled.tsx"; -import { usePopupState } from "material-ui-popup-state/hooks"; import { bindHover, bindPopover } from "material-ui-popup-state"; +import { usePopupState } from "material-ui-popup-state/hooks"; +import { useMemo } from "react"; +import { useTranslation } from "react-i18next"; +import { TaskStatus } from "../../../api/workflow.ts"; +import FacebookCircularProgress from "../../Common/CircularProgress.tsx"; +import CheckCircleFilled from "../../Icons/CheckCircleFilled.tsx"; +import DismissCircleFilled from "../../Icons/DismissCircleFilled.tsx"; import StepProgressPopover from "./StepProgressPopover.tsx"; interface ProgressStepIconProps extends StepIconProps {} -const ProgressStepIcon = - (task: TaskResponse) => (props: ProgressStepIconProps) => { - const { active, completed, icon, ...rest } = props; +const ProgressStepIcon = (status: string) => (props: ProgressStepIconProps) => { + const { active, completed, icon, ...rest } = props; - let newIcon = icon; - if (active) { - newIcon = ; - if (task.status == TaskStatus.error) { - newIcon = ( - theme.palette.error.main }} - /> - ); - } - } else if (completed) { - newIcon = ( - theme.palette.primary.main }} - /> - ); + let newIcon = icon; + if (active) { + newIcon = ; + if (status == TaskStatus.error) { + newIcon = theme.palette.error.main }} />; } + } else if (completed) { + newIcon = theme.palette.primary.main }} />; + } - if (active && task.status == TaskStatus.error) { - newIcon = ( - theme.palette.error.main }} - /> - ); - } + if (active && status == TaskStatus.error) { + newIcon = theme.palette.error.main }} />; + } - if (active && task.status == TaskStatus.canceled) { - newIcon = ( - theme.palette.action.active, - }} - /> - ); - } - - return ( - theme.palette.action.active, + }} /> ); - }; + } + + return ; +}; export interface TaskProgressStepProps extends StepProps { - task: TaskResponse; + taskId: string; + taskStatus: string; title: string; description?: string; showProgress?: boolean; @@ -78,7 +53,8 @@ export interface TaskProgressStepProps extends StepProps { } const TaskProgressStep = ({ - task, + taskId, + taskStatus, title, description, showProgress, @@ -87,36 +63,27 @@ const TaskProgressStep = ({ }: TaskProgressStepProps) => { const popupState = usePopupState({ variant: "popover", - popupId: `progress_${task.id}_${title}`, + popupId: `progress_${taskId}_${title}`, }); const { open, ...restPopup } = bindPopover(popupState); const StepIconComponent = useMemo(() => { - return ProgressStepIcon(task); - }, [task.status]); + return ProgressStepIcon(taskStatus); + }, [taskStatus]); const { t } = useTranslation(); return ( <> - + {t(description)} - ) : undefined - } + optional={description ? {t(description)} : undefined} slots={{ - stepIcon: StepIconComponent + stepIcon: StepIconComponent, }} > {t(title)} - {showProgress && progressing && ( - - )} + {showProgress && progressing && } ); };