Skip to content

Commit

Permalink
feat: Improve UX for Single Task Executions (#103)
Browse files Browse the repository at this point in the history
* refactor: make workflow details generic so it can be used for tasks (#96)

* refactor: make workflow details generic so it can be used for tasks

* chore: cleanup and moving tests over

* feat: adding route and navigation to the task details page (#97)

* Refactor Launch form to use a state machine (#99)

* refactor: filling out details of the state machine for launch

* refactor: checkpoint

* refactor: mostly finished wiring of machine to component state

* refactor: more work to get form component migrated to using machine

* refactor: cleaning up state for selectors

* fix: type error due to patch version upgrade

* refactor: trying a flat state structure

* fix: getting all tests passing again

* chore: cleanup and docs

* chore: pull request feedback

* refactor: Make launch state and components generic (#100)

* refactor: splitting launch machine into two separate types

* refactor: move shared state out to component

* refactor: use composition to create workflow form

* refactor: update usage of LaunchWorkflowForm -> LaunchForm

* chore: cleanup and fix tests

* feat: Add Task support to Launch form (#101)

* feat: add task support in launch components

* test: updating launch form tests to handle task cases

* fix: remaining broken tests

* Cleanup work for launching single task executions. (#102)

* feat: add support for launch tasks in entity details view

* fix: correctly map initial parameters when relaunching

* fix: correct parent name and back link in execution details page

* fix: pass through referenceExecution when relaunching

* test: check rendering of referenceExecution

* test: adding tests for relaunch flow
  • Loading branch information
schottra authored Oct 8, 2020
1 parent b16d28e commit d0335dc
Show file tree
Hide file tree
Showing 96 changed files with 6,457 additions and 3,016 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@
"@types/webpack-env": "^1.13.1",
"@types/webpack-hot-middleware": "^2.15.0",
"@typescript-eslint/parser": "^1.0.0",
"@xstate/react": "^0.8.1",
"@xstate/react": "^1.0.0",
"add-asset-html-webpack-plugin": "^2.1.3",
"autoprefixer": "^8.3.0",
"axios": "^0.18.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import { makeStyles, Theme } from '@material-ui/core/styles';
import * as classnames from 'classnames';
import { WaitForData } from 'components';
import { useCommonStyles } from 'components/common/styles';
import { useWorkflowNamedEntity } from 'components/hooks/useNamedEntity';
import { NamedEntityIdentifier, NamedEntityMetadata } from 'models';
import { useNamedEntity } from 'components/hooks/useNamedEntity';
import { NamedEntityMetadata, ResourceIdentifier } from 'models';
import * as React from 'react';
import reactLoadingSkeleton from 'react-loading-skeleton';
import { noDescriptionStrings } from './constants';

const Skeleton = reactLoadingSkeleton;

Expand All @@ -16,21 +17,18 @@ const useStyles = makeStyles((theme: Theme) => ({
}
}));

const noDescriptionString = 'This workflow has no description.';

/** Fetches and renders the description for a given workflow ID */
export const WorkflowDescription: React.FC<{
workflowId: NamedEntityIdentifier;
}> = ({ workflowId }) => {
/** Fetches and renders the description for a given Entity (LaunchPlan,Workflow,Task) ID */
export const EntityDescription: React.FC<{
id: ResourceIdentifier;
}> = ({ id }) => {
const commonStyles = useCommonStyles();
const styles = useStyles();
const namedEntity = useWorkflowNamedEntity(workflowId);
const namedEntity = useNamedEntity(id);
const { metadata = {} as NamedEntityMetadata } = namedEntity.value;
const hasDescription = !!metadata.description;
return (
<>
<Typography variant="h6">Description</Typography>

<Typography variant="body2" className={styles.description}>
<WaitForData
{...namedEntity}
Expand All @@ -44,7 +42,7 @@ export const WorkflowDescription: React.FC<{
>
{hasDescription
? metadata.description
: noDescriptionString}
: noDescriptionStrings[id.resourceType]}
</span>
</WaitForData>
</Typography>
Expand Down
102 changes: 102 additions & 0 deletions src/components/Entities/EntityDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { Dialog } from '@material-ui/core';
import { makeStyles, Theme } from '@material-ui/core/styles';
import { contentMarginGridUnits } from 'common/layout';
import { WaitForData } from 'components/common';
import { EntityDescription } from 'components/Entities/EntityDescription';
import { useProject } from 'components/hooks';
import { LaunchForm } from 'components/Launch/LaunchForm/LaunchForm';
import { ResourceIdentifier, ResourceType } from 'models';
import * as React from 'react';
import { entitySections } from './constants';
import { EntityDetailsHeader } from './EntityDetailsHeader';
import { EntityExecutions } from './EntityExecutions';
import { EntitySchedules } from './EntitySchedules';

const useStyles = makeStyles((theme: Theme) => ({
metadataContainer: {
display: 'flex',
marginBottom: theme.spacing(5),
marginTop: theme.spacing(2),
width: '100%'
},
descriptionContainer: {
flex: '2 1 auto',
marginRight: theme.spacing(2)
},
executionsContainer: {
display: 'flex',
flex: '1 1 auto',
flexDirection: 'column',
margin: `0 -${theme.spacing(contentMarginGridUnits)}px`
},
schedulesContainer: {
flex: '1 2 auto',
marginRight: theme.spacing(30)
}
}));

export interface EntityDetailsProps {
id: ResourceIdentifier;
}

function getLaunchProps(id: ResourceIdentifier) {
if (id.resourceType === ResourceType.TASK) {
return { taskId: id };
}

return { workflowId: id };
}

/** A view which optionally renders description, schedules, executions, and a
* launch button/form for a given entity. Note: not all components are suitable
* for use with all entities (not all entities have schedules, for example).
*/
export const EntityDetails: React.FC<EntityDetailsProps> = ({ id }) => {
const sections = entitySections[id.resourceType];
const project = useProject(id.project);
const styles = useStyles();
const [showLaunchForm, setShowLaunchForm] = React.useState(false);
const onLaunch = () => setShowLaunchForm(true);
const onCancelLaunch = () => setShowLaunchForm(false);

return (
<WaitForData {...project}>
<EntityDetailsHeader
project={project.value}
id={id}
launchable={!!sections.launch}
onClickLaunch={onLaunch}
/>
<div className={styles.metadataContainer}>
{!!sections.description ? (
<div className={styles.descriptionContainer}>
<EntityDescription id={id} />
</div>
) : null}
{!!sections.schedules ? (
<div className={styles.schedulesContainer}>
<EntitySchedules id={id} />
</div>
) : null}
</div>
{!!sections.executions ? (
<div className={styles.executionsContainer}>
<EntityExecutions id={id} />
</div>
) : null}
{!!sections.launch ? (
<Dialog
scroll="paper"
maxWidth="sm"
fullWidth={true}
open={showLaunchForm}
>
<LaunchForm
onClose={onCancelLaunch}
{...getLaunchProps(id)}
/>
</Dialog>
) : null}
</WaitForData>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import { makeStyles, Theme } from '@material-ui/core/styles';
import ArrowBack from '@material-ui/icons/ArrowBack';
import * as classnames from 'classnames';
import { useCommonStyles } from 'components/common/styles';
import { Project } from 'models';
import { Project, ResourceIdentifier } from 'models';
import { getProjectDomain } from 'models/Project/utils';
import * as React from 'react';
import { Link } from 'react-router-dom';
import { Routes } from 'routes';
import { launchStrings } from './constants';
import { backUrlGenerator } from './generators';

const useStyles = makeStyles((theme: Theme) => ({
actionsContainer: {},
Expand All @@ -28,31 +29,24 @@ const useStyles = makeStyles((theme: Theme) => ({
}
}));

export interface WorkflowDetailsRouteParams {
projectId: string;
domainId: string;
workflowName: string;
}
export type WorkflowDetailsProps = WorkflowDetailsRouteParams;

interface WorkflowDetailsHeaderProps {
domainId: string;
interface EntityDetailsHeaderProps {
project: Project;
workflowName: string;
onClickLaunch(): void;
id: ResourceIdentifier;
launchable?: boolean;
onClickLaunch?(): void;
}

/** Renders the workflow name and actions shown on the workflow details page */
export const WorkflowDetailsHeader: React.FC<WorkflowDetailsHeaderProps> = ({
domainId,
/** Renders the entity name and any applicable actions. */
export const EntityDetailsHeader: React.FC<EntityDetailsHeaderProps> = ({
id,
onClickLaunch,
project,
workflowName
launchable = false,
project
}) => {
const styles = useStyles();
const commonStyles = useCommonStyles();
const domain = getProjectDomain(project, domainId);
const headerText = `${domain.name} / ${workflowName}`;
const domain = getProjectDomain(project, id.domain);
const headerText = `${domain.name} / ${id.name}`;
return (
<div className={styles.headerContainer}>
<div
Expand All @@ -63,24 +57,23 @@ export const WorkflowDetailsHeader: React.FC<WorkflowDetailsHeaderProps> = ({
>
<Link
className={commonStyles.linkUnstyled}
to={Routes.ProjectDetails.sections.workflows.makeUrl(
project.id,
domainId
)}
to={backUrlGenerator[id.resourceType](id)}
>
<ArrowBack color="inherit" />
</Link>
<span className={styles.headerText}>{headerText}</span>
</div>
<div className={styles.actionsContainer}>
<Button
color="primary"
id="launch-workflow"
onClick={onClickLaunch}
variant="contained"
>
Launch Workflow
</Button>
{launchable ? (
<Button
color="primary"
id="launch-workflow"
onClick={onClickLaunch}
variant="contained"
>
{launchStrings[id.resourceType]}
</Button>
) : null}
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import { makeStyles, Theme } from '@material-ui/core/styles';
import { contentMarginGridUnits } from 'common/layout';
import { WaitForData } from 'components/common';
import { ExecutionFilters } from 'components/Executions/ExecutionFilters';
import { useWorkflowExecutionFiltersState } from 'components/Executions/filters/useExecutionFiltersState';
import { WorkflowExecutionsTable } from 'components/Executions/Tables/WorkflowExecutionsTable';
import { useWorkflowExecutions } from 'components/hooks';
import { NamedEntityIdentifier } from 'models';
import { FilterOperationName, SortDirection } from 'models/AdminEntity';
import { useWorkflowExecutionFiltersState as useExecutionFiltersState } from 'components/Executions/filters/useExecutionFiltersState';
import { WorkflowExecutionsTable as ExecutionsTable } from 'components/Executions/Tables/WorkflowExecutionsTable';
import { useWorkflowExecutions as useExecutions } from 'components/hooks';
import { ResourceIdentifier } from 'models';
import { SortDirection } from 'models/AdminEntity';
import { executionSortFields } from 'models/Execution';
import * as React from 'react';
import { executionFilterGenerator } from './generators';

const useStyles = makeStyles((theme: Theme) => ({
filtersContainer: {
Expand All @@ -21,33 +22,30 @@ const useStyles = makeStyles((theme: Theme) => ({
}
}));

export interface WorkflowExecutionsProps {
workflowId: NamedEntityIdentifier;
export interface EntityExecutionsProps {
id: ResourceIdentifier;
}

/** The tab/page content for viewing a workflow's executions */
export const WorkflowExecutions: React.FC<WorkflowExecutionsProps> = ({
workflowId: { project, domain, name }
}) => {
export const EntityExecutions: React.FC<EntityExecutionsProps> = ({ id }) => {
const { domain, project, resourceType } = id;
const styles = useStyles();
const filtersState = useWorkflowExecutionFiltersState();
const filtersState = useExecutionFiltersState();
const sort = {
key: executionSortFields.createdAt,
direction: SortDirection.DESCENDING
};

const executions = useWorkflowExecutions(
const baseFilters = React.useMemo(
() => executionFilterGenerator[resourceType](id),
[id]
);

const executions = useExecutions(
{ domain, project },
{
sort,
filter: [
{
key: 'workflow.name',
operation: FilterOperationName.EQ,
value: name
},
...filtersState.appliedFilters
]
filter: [...baseFilters, ...filtersState.appliedFilters]
}
);

Expand All @@ -60,7 +58,7 @@ export const WorkflowExecutions: React.FC<WorkflowExecutionsProps> = ({
<ExecutionFilters {...filtersState} />
</div>
<WaitForData {...executions}>
<WorkflowExecutionsTable {...executions} />
<ExecutionsTable {...executions} />
</WaitForData>
</>
);
Expand Down
Loading

0 comments on commit d0335dc

Please sign in to comment.