Skip to content
This repository has been archived by the owner on Jul 23, 2024. It is now read-only.

Commit

Permalink
Merge pull request #181 from parodos-dev/FLPATH-478/revisit-options
Browse files Browse the repository at this point in the history
Allow the user to revisit the workflow options at anytime
  • Loading branch information
RichardW98 committed Jun 30, 2023
2 parents 44e149d + 525dcc9 commit 953d5f9
Show file tree
Hide file tree
Showing 15 changed files with 250 additions and 36 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react';
import { makeStyles, Typography } from '@material-ui/core';
import { Link, Breadcrumbs } from '@backstage/core-components';

interface AssessmentBreadCrumbProps {
linkText?: string;
projectId: string;
executionId: string;
current: string;
}

const useStyles = makeStyles(theme => ({
link: {
color: theme.palette.primary.main,
},
}));

export function AssessmentBreadCrumb({
projectId,
executionId,
linkText = 'Assessment results',
current,
}: AssessmentBreadCrumbProps): JSX.Element {
const styles = useStyles();

return (
<Breadcrumbs aria-label="breadcrumb">
<Link
className={styles.link}
to={`/parodos/workflows/assessment/${projectId}/${executionId}`}
>
{linkText}
</Link>
<Typography>{current}</Typography>
</Breadcrumbs>
);
}
5 changes: 5 additions & 0 deletions plugins/parodos/src/components/PluginRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { WorkflowsOverview } from './workflow/WorkflowsOverview';
import { ProjectsOverview } from './projects/ProjectsOverview';
import { ProjectsNew } from './projects/New/ProjectsNew';
import { ProjectAccessManager } from './projects/ProjectAccessManager';
import { AssessmentWorkflow } from './workflow/Assessment/AssessmentWorkflow';

export const PluginRouter = () => (
<Routes>
Expand All @@ -19,6 +20,10 @@ export const PluginRouter = () => (
element={<ProjectAccessManager />}
/>
<Route path="/workflows" element={<WorkflowsOverview />} />
<Route
path="/workflows/assessment/:projectId/:assessmentWorkflowExecutionId"
element={<AssessmentWorkflow />}
/>
<Route path="/onboarding" element={<Workflow />} />
<Route path="/notification" element={<Notification />} />
<Route
Expand Down
1 change: 1 addition & 0 deletions plugins/parodos/src/components/TestApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export const TestApp: React.FC = ({ children }) => {
JSON.stringify([
{
workFlowExecutionId: '511da8ce-4df7-438b-a9ec-0130f14884bd',
workFlowType: 'INFRASTRUCTURE',
projectId: '511da8ce-4df7-438b-a9ec-0130f14884bd',
workFlowName: 'myWorkflow',
workStatus: 'IN_PROGRESS',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export function ProjectsOverview(): JSX.Element {
title="There are no projects to display."
description="Want to learn more about Parodos? Check out our documentation."
action={
<Button variant="contained" type="button" color="primary">
<Button variant="contained" type="button" color="secondary">
Docs
</Button>
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/* eslint-disable no-console */
import React, { useEffect } from 'react';
import {
Breadcrumbs,
ContentHeader,
EmptyState,
InfoCard,
Link,
Progress,
SupportButton,
} from '@backstage/core-components';
import { useParams } from 'react-router-dom';
import { ParodosPage } from '../../ParodosPage';
import { Box, Grid, makeStyles, Typography } from '@material-ui/core';
import { assert } from 'assert-ts';
import { WorkflowOptionsList } from '../WorkflowOptionsList';
import useAsync from 'react-use/lib/useAsync';
import { useStore } from '../../../stores/workflowStore/workflowStore';
import { errorApiRef, fetchApiRef, useApi } from '@backstage/core-plugin-api';
import { getWorkflowOptions } from '../hooks/getWorkflowOptions';
import * as urls from '../../../urls';

const useStyles = makeStyles(theme => ({
fullHeight: {
height: '100%',
},
link: {
color: theme.palette.primary.main,
},
}));

export function AssessmentWorkflow(): JSX.Element {
const styles = useStyles();
const { projectId, assessmentWorkflowExecutionId } = useParams();
const errorApi = useApi(errorApiRef);
const { fetch } = useApi(fetchApiRef);
const workflowsUrl = useStore(state => state.getApiUrl(urls.Workflows));

assert(!!projectId, `no projectId for assessment workflow`);
assert(
!!assessmentWorkflowExecutionId,
`no assessmentWorkflowExecutionId for assessment workflow`,
);

const selectedProject = useStore(state => state.getProjectById(projectId));

const {
loading,
error,
value: workflowOptions,
} = useAsync(async () => {
return await getWorkflowOptions(fetch, {
workflowsUrl,
executionId: assessmentWorkflowExecutionId,
});
}, [assessmentWorkflowExecutionId, fetch, workflowsUrl]);

// console.log(workflowOptions);

useEffect(() => {
if (error) {
errorApi.post(new Error(`Failure retrieving workflow options`));
}
}, [error, errorApi]);

const noOptions =
loading === false && workflowOptions && workflowOptions.length === 0;
const hasOptions = workflowOptions && workflowOptions.length > 0;

return (
<ParodosPage stretch>
<ContentHeader title="Assessment">
<SupportButton title="Need help?">Lorem Ipsum</SupportButton>
</ContentHeader>
<Box mb={3}>
<Breadcrumbs aria-label="breadcrumb">
<Link className={styles.link} to="/parodos/workflows">
Workflows
</Link>
<Typography>Assessment results</Typography>
</Breadcrumbs>
</Box>
<InfoCard className={styles.fullHeight}>
<Grid container direction="row">
<Grid item xs={12}>
{loading && <Progress />}
{noOptions && (
<EmptyState
missing="data"
title="There are no further workflow options to complete for this assessment."
/>
)}
{hasOptions && (
<WorkflowOptionsList
isNew={false}
project={selectedProject}
workflowOptions={workflowOptions}
assessmentWorkflowExecutionId={assessmentWorkflowExecutionId}
/>
)}
</Grid>
</Grid>
</InfoCard>
</ParodosPage>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { useCommonStyles } from '../../styles';
import { Link } from 'react-router-dom';
import { type Project } from '../../models/project';
import { WorkflowOptionsListItem } from './hooks/useCreateWorkflow';
import cs from 'classnames';

interface WorkflowOptionsListProps {
project: Project;
Expand All @@ -35,6 +36,9 @@ const useStyles = makeStyles(theme => ({
color: theme.palette.text.secondary,
height: '100%',
},
recommended: {
border: `2px solid ${theme.palette.primary.main}`,
},
}));

export function WorkflowOptionsList({
Expand All @@ -59,7 +63,9 @@ export function WorkflowOptionsList({
{workflowOptions.map(workflowOption => (
<Grid item xs={12} lg={6} xl={4} key={workflowOption.identifier}>
<Card
className={styles.applicationCard}
className={cs(styles.applicationCard, {
[styles.recommended]: workflowOption.recommended,
})}
variant="elevation"
elevation={3}
>
Expand Down
9 changes: 8 additions & 1 deletion plugins/parodos/src/components/workflow/WorkflowsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ export const WorkflowsTable: React.FC<{
? formatDate.format(Date.parse(workflow.endDate))
: undefined,
additionalInfos: workflow.additionalInfos,
workFlowType: workflow.workFlowType,
} as WorkflowTableData;
})
.filter(workflow =>
Expand Down Expand Up @@ -296,7 +297,13 @@ export const WorkflowsTable: React.FC<{
<TableCell>
<LinkButton
color="primary"
to={`${pluginRoutePrefix}/onboarding/${projectId}/${rowData.id}/workflow-detail`}
to={`${pluginRoutePrefix}/onboarding/${projectId}/${
rowData.id
}/workflow-detail${
rowData.workFlowType === 'ASSESSMENT'
? `?assessmentexecutionid=${rowData.id}`
: ''
}`}
>
VIEW
</LinkButton>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,45 @@
import { FetchApi } from '@backstage/core-plugin-api';
import { WorkflowOptions, workflowSchema } from '../../../models/workflow';
import {
displayableWorkflowOptions,
workflowSchema,
} from '../../../models/workflow';
import { taskDisplayName } from '../../../utils/string';
import { WorkflowOptionsListItem } from './useCreateWorkflow';

export async function getWorkflowOptions(
fetch: FetchApi['fetch'],
options: { workflowsUrl: string; executionId: string },
): Promise<Partial<WorkflowOptions>> {
): Promise<WorkflowOptionsListItem[]> {
const response = await fetch(
`${options.workflowsUrl}/${options.executionId}/context?param=WORKFLOW_OPTIONS`,
);
if (!response.ok) {
throw new Error(response.statusText);
}
return workflowSchema.parse(await response.json()).workFlowOptions;
const workflowOptions = workflowSchema.parse(
await response.json(),
).workFlowOptions;

return displayableWorkflowOptions
.flatMap(option => {
const items = workflowOptions[option] ?? [];

if (items.length === 0) {
return items;
}

const optionType = taskDisplayName(option);

return items.map(item => ({
...item,
type: optionType,
}));
})
.sort((left, right) => {
if (left.recommended === right.recommended) {
return left.workFlowName.localeCompare(right.workFlowName);
}

return left.recommended ? -1 : 0;
}) as WorkflowOptionsListItem[];
}
26 changes: 5 additions & 21 deletions plugins/parodos/src/components/workflow/hooks/useCreateWorkflow.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import useAsyncFn, { AsyncFnReturn } from 'react-use/lib/useAsyncFn';
import {
displayableWorkflowOptions,
WorkflowOptionItem,
} from '../../../models/workflow';
import { WorkflowOptionItem } from '../../../models/workflow';
import * as urls from '../../../urls';
import { taskDisplayName } from '../../../utils/string';
import type { Project } from '../../../models/project';
import { useStore } from '../../../stores/workflowStore/workflowStore';
import { getWorkflowOptions } from './getWorkflowOptions';
Expand Down Expand Up @@ -54,22 +50,10 @@ export function useCreateWorkflow(assessment: string): AsyncFnReturn<
executionId: workFlowExecutionId,
});

const options = displayableWorkflowOptions.flatMap(option => {
const items = workflowOptions[option] ?? [];

if (items.length === 0) {
return items;
}

const optionType = taskDisplayName(option);

return items.map(item => ({
...item,
type: optionType,
}));
}) as WorkflowOptionsListItem[];

return { options, assessmentWorkflowExecutionId: workFlowExecutionId };
return {
options: workflowOptions,
assessmentWorkflowExecutionId: workFlowExecutionId,
};
},
[
executeWorkflow,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,18 @@ export function useStartWorkflow({
});

navigate(
`/parodos/onboarding/${projectId}/${workFlowExecutionId}/workflow-detail`,
`/parodos/onboarding/${projectId}/${workFlowExecutionId}/workflow-detail?assessmentexecutionid=${assessmentWorkflowExecutionId}`,
{
state: { isNew },
},
);
},
[projectId, navigate, isNew, executeWorkflow],
[
executeWorkflow,
projectId,
navigate,
assessmentWorkflowExecutionId,
isNew,
],
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ import {
} from '@backstage/core-components';
import { Link, useParams, useSearchParams } from 'react-router-dom';
import { ParodosPage } from '../../ParodosPage';
import { Button, Chip, makeStyles, Typography } from '@material-ui/core';
import { Box, Button, Chip, makeStyles, Typography } from '@material-ui/core';
import { useWorkflowDefinitionToJsonSchema } from '../../../hooks/useWorkflowDefinitionToJsonSchema/useWorkflowDefinitionToJsonSchema';
import { assert } from 'assert-ts';
import { Form } from '../../Form/Form';
import { type RJSFValidationError } from '@rjsf/utils';
import { errorApiRef, useApi } from '@backstage/core-plugin-api';
import { useStartWorkflow } from '../hooks/useStartWorkflow';
import { Empty } from './Empty';
import { AssessmentBreadCrumb } from '../../AssessmentBreadCrumb/AssessmentBreadCrumb';

interface OnboardingProps {
isNew: boolean;
Expand Down Expand Up @@ -79,7 +80,13 @@ export function Onboarding({ isNew }: OnboardingProps): JSX.Element {
<ContentHeader title={`${workflowOption}`}>
<SupportButton title="Need help?">Lorem Ipsum</SupportButton>
</ContentHeader>
<Typography paragraph>You are onboarding {workflowOption}.</Typography>
<Box mb={3}>
<AssessmentBreadCrumb
projectId={projectId}
executionId={assessmentWorkflowExecutionId}
current="Onboarding"
/>
</Box>
{loading && <Progress />}
{formSchema.steps.length === 0 && <Empty startWorkflow={startWorkflow} />}
{formSchema.steps.length > 0 && (
Expand Down
Loading

0 comments on commit 953d5f9

Please sign in to comment.