diff --git a/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx b/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx index 2f22cb9849..820e65d126 100644 --- a/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx +++ b/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx @@ -15,7 +15,7 @@ */ import { Page, Header, Lifecycle, Content } from '@backstage/core'; -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, memo } from 'react'; import { makeStyles, Theme, @@ -34,7 +34,11 @@ import Check from '@material-ui/icons/Check'; import Cancel from '@material-ui/icons/Cancel'; import Typography from '@material-ui/core/Typography'; import { useParams } from 'react-router'; -import { useTaskEventStream, TaskStream } from '../hooks/useEventStream'; +import { + useTaskEventStream, + TaskStream, + Status, +} from '../hooks/useEventStream'; import LazyLog from 'react-lazylog/build/LazyLog'; import { StepButton, StepIconProps } from '@material-ui/core'; @@ -130,36 +134,29 @@ const useStyles = makeStyles((theme: Theme) => }), ); -export const TaskStepper = ({ taskStream }: { taskStream: TaskStream }) => { +type Steps = { + log: string[]; + id: string; + name: string; + status: Status; +}; + +export const TaskStepper = ({ steps }: { steps: Steps[] }) => { const classes = useStyles(); const [activeStep, setActiveStep] = useState(0); const [expandAll, setExpandAll] = useState(false); - const steps = taskStream?.task?.spec.steps ?? []; - - const handleNext = () => { - setActiveStep(prevActiveStep => prevActiveStep + 1); - }; - - const handleBack = () => { - setActiveStep(prevActiveStep => prevActiveStep - 1); - }; - const handleStep = (step: number) => { setExpandAll(false); setActiveStep(step); }; - const handleReset = () => { - setActiveStep(0); - }; - useEffect(() => { - const activeIndex = Object.values(taskStream?.steps ?? {}).findIndex(step => + const activeIndex = steps.findIndex(step => ['failed', 'processing'].includes(step.status), ); - setActiveStep(activeIndex); - }, [taskStream]); + setActiveStep(2); + }, [steps]); return (
@@ -170,12 +167,24 @@ export const TaskStepper = ({ taskStream }: { taskStream: TaskStream }) => { {steps.map((step, index) => { - const isCompleted = - taskStream.steps?.[step.id].status === 'completed'; - const isFailed = taskStream.steps?.[step.id].status === 'failed'; + const isCompleted = step.status === 'completed'; + const isFailed = step.status === 'failed'; + // return ( + // + // ); + return ( - - handleStep(index)}> + + {}}> {
@@ -200,21 +205,62 @@ export const TaskStepper = ({ taskStream }: { taskStream: TaskStream }) => { ); })}
- {activeStep === steps.length && ( - - All steps completed - you're finished - - - )}
); }; +type TaskStepOptions = { + name: string; + isCompleted: boolean; + isFailed: boolean; + expanded: boolean; + handleStep: () => void; + index: number; + log: string; +}; + +export const TaskStep = memo( + ({ + log, + name, + isCompleted, + isFailed, + handleStep, + index, + expanded, + }: TaskStepOptions) => { + const onClick = React.useCallback(() => handleStep(index), [ + handleStep, + index, + ]); + return ( + + + + {name} + + + + +
+ +
+
+
+ ); + }, +); export const TaskPage = () => { const { taskId } = useParams(); const taskStream = useTaskEventStream(taskId); + const steps = + taskStream.task?.spec.steps.map(step => ({ + ...step, + ...taskStream?.steps?.[step.id], + })) ?? []; return ( @@ -228,7 +274,7 @@ export const TaskPage = () => { subtitle={`Activity for task: ${taskId}`} /> - + ); diff --git a/plugins/scaffolder/src/components/hooks/useEventStream.ts b/plugins/scaffolder/src/components/hooks/useEventStream.ts index 2cbf8b4f83..20338463c3 100644 --- a/plugins/scaffolder/src/components/hooks/useEventStream.ts +++ b/plugins/scaffolder/src/components/hooks/useEventStream.ts @@ -14,16 +14,16 @@ * limitations under the License. */ import { useImmerReducer } from 'use-immer'; -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; import { scaffolderApiRef } from '../../api'; import { ScaffolderTask } from '../../types'; +import { useDebounce } from 'react-use'; import { Subscription, useApi } from '@backstage/core'; -type Status = 'open' | 'processing' | 'failed' | 'completed'; +export type Status = 'open' | 'processing' | 'failed' | 'completed'; type Step = { id: string; status: Status; - log: string[]; endedAt?: string; startedAt?: string; }; @@ -34,7 +34,7 @@ export type TaskStream = { log: string[]; completed: boolean; task?: ScaffolderTask; - steps?: { [stepId in string]: Step }; + steps: { [stepId in string]: Step }; }; type ReducerAction = @@ -56,7 +56,7 @@ function reducer(draft: TaskStream, action: ReducerAction) { switch (action.type) { case 'INIT': { draft.steps = action.data.spec.steps.reduce((current, next) => { - current[next.id] = { log: [], status: 'open', id: next.id }; + current[next.id] = { status: 'open', id: next.id }; return current; }, {} as { [stepId in string]: Step }); draft.loading = false; @@ -72,12 +72,7 @@ function reducer(draft: TaskStream, action: ReducerAction) { const currentStep = draft.steps?.[stepId]; const logLine = `${action.data.createdAt} ${action.data.body.message}`; - if (!currentStep) { - draft.log.push(logLine); - return; - } - - currentStep.log.push(logLine); + draft.log.push(logLine); if ( action.data.body.status && @@ -89,9 +84,7 @@ function reducer(draft: TaskStream, action: ReducerAction) { currentStep.startedAt = action.data.createdAt; } - if ( - ['zcancelled', 'failed', 'completed'].includes(currentStep.status) - ) { + if (['cancelled', 'failed', 'completed'].includes(currentStep.status)) { currentStep.endedAt = action.data.createdAt; } } @@ -124,7 +117,14 @@ export const useTaskEventStream = (taskId: string): TaskStream => { log: [], steps: {} as { [stepId in string]: Step }, }); - + // const [debouncedState, setDebouncedState] = useState(state); + // useDebounce( + // () => { + // setDebouncedState(state); + // }, + // 1000, + // [state], + // ); useEffect(() => { let didCancel = false; let subscription: Subscription | undefined; @@ -167,4 +167,5 @@ export const useTaskEventStream = (taskId: string): TaskStream => { }, [scaffolderApi, dispatch, taskId]); return state; + // return debouncedState; };