Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/version details #198

Merged
merged 4 commits into from
Sep 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 34 additions & 17 deletions src/components/Entities/EntityDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { EntityDetailsHeader } from './EntityDetailsHeader';
import { EntityExecutions } from './EntityExecutions';
import { EntitySchedules } from './EntitySchedules';
import { EntityVersions } from './EntityVersions';
import classNames from 'classnames';

const useStyles = makeStyles((theme: Theme) => ({
metadataContainer: {
Expand All @@ -35,6 +36,9 @@ const useStyles = makeStyles((theme: Theme) => ({
flexDirection: 'column',
height: theme.spacing(45)
},
versionView: {
flex: '1 1 auto'
},
schedulesContainer: {
flex: '1 2 auto',
marginRight: theme.spacing(30)
Expand All @@ -43,6 +47,7 @@ const useStyles = makeStyles((theme: Theme) => ({

export interface EntityDetailsProps {
id: ResourceIdentifier;
versionView?: boolean;
}

function getLaunchProps(id: ResourceIdentifier) {
Expand All @@ -53,11 +58,17 @@ function getLaunchProps(id: ResourceIdentifier) {
return { workflowId: id };
}

/** A view which optionally renders description, schedules, executions, and a
/**
* 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).
* @param id
* @param versionView
*/
export const EntityDetails: React.FC<EntityDetailsProps> = ({ id }) => {
export const EntityDetails: React.FC<EntityDetailsProps> = ({
id,
versionView = false
}) => {
const sections = entitySections[id.resourceType];
const project = useProject(id.project);
const styles = useStyles();
Expand All @@ -73,24 +84,30 @@ export const EntityDetails: React.FC<EntityDetailsProps> = ({ 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>
{!versionView && (
<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.versions ? (
<div className={styles.versionsContainer}>
<EntityVersions id={id} />
<div
className={classNames(styles.versionsContainer, {
[styles.versionView]: versionView
})}
>
<EntityVersions id={id} versionView={versionView} />
</div>
) : null}
{sections.executions ? (
{sections.executions && !versionView ? (
<div className={styles.executionsContainer}>
<EntityExecutions id={id} />
</div>
Expand Down
49 changes: 37 additions & 12 deletions src/components/Entities/EntityVersions.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Typography } from '@material-ui/core';
import Typography from '@material-ui/core/Typography';
import { makeStyles, Theme } from '@material-ui/core/styles';
import { contentMarginGridUnits } from 'common/layout';
import { WaitForData } from 'components/common/WaitForData';
Expand All @@ -10,6 +10,8 @@ import { interactiveTextColor } from 'components/Theme/constants';
import { SortDirection } from 'models/AdminEntity/types';
import { ResourceIdentifier } from 'models/Common/types';
import { executionSortFields } from 'models/Execution/constants';
import { Routes } from 'routes/routes';
import { history } from 'routes/history';
import * as React from 'react';
import { executionFilterGenerator } from './generators';
import { WorkflowVersionsTablePageSize } from './constants';
Expand All @@ -32,14 +34,19 @@ const useStyles = makeStyles((theme: Theme) => ({

export interface EntityVersionsProps {
id: ResourceIdentifier;
versionView?: boolean;
}

/**
* The tab/page content for viewing a workflow's versions.
* @param id
* @param versionView
*/
export const EntityVersions: React.FC<EntityVersionsProps> = ({ id }) => {
const { domain, project, resourceType } = id;
export const EntityVersions: React.FC<EntityVersionsProps> = ({
id,
versionView = false
}) => {
const { domain, project, resourceType, name } = id;
const styles = useStyles();
const filtersState = useWorkflowExecutionFiltersState();
const sort = {
Expand All @@ -57,29 +64,47 @@ export const EntityVersions: React.FC<EntityVersionsProps> = ({ id }) => {
{
sort,
filter: [...baseFilters, ...filtersState.appliedFilters],
limit: WorkflowVersionsTablePageSize
limit: versionView ? 100 : WorkflowVersionsTablePageSize
}
);

const handleViewAll = React.useCallback(() => {
history.push(
Routes.WorkflowVersionDetails.makeUrl(
project,
domain,
name,
versions.value[0].id.version ?? ''
)
);
}, [project, domain, name, versions]);

/** Don't render component until finish fetching user profile */
if (filtersState.filters[4].status !== 'LOADED') {
return null;
}

return (
<>
<div className={styles.headerContainer}>
<Typography className={styles.header} variant="h6">
Recent Workflow Versions
</Typography>
<Typography className={styles.viewAll} variant="body1">
View All
</Typography>
</div>
{!versionView && (
<div className={styles.headerContainer}>
<Typography className={styles.header} variant="h6">
Recent Workflow Versions
</Typography>
<Typography
className={styles.viewAll}
variant="body1"
onClick={handleViewAll}
>
View All
</Typography>
</div>
)}
<WaitForData {...versions}>
<WorkflowVersionsTable
{...versions}
isFetching={isLoadingState(versions.state)}
versionView={versionView}
/>
</WaitForData>
</>
Expand Down
13 changes: 12 additions & 1 deletion src/components/Executions/Tables/ExecutionsTableHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,25 @@ import { ColumnDefinition } from './types';
export const ExecutionsTableHeader: React.FC<{
columns: ColumnDefinition<any>[];
scrollbarPadding?: number;
}> = ({ columns, scrollbarPadding = 0 }) => {
versionView?: boolean;
}> = ({ columns, scrollbarPadding = 0, versionView = false }) => {
const tableStyles = useExecutionTableStyles();
const scrollbarSpacer =
scrollbarPadding > 0 ? (
<div style={{ width: scrollbarPadding }} />
) : null;
return (
<div className={tableStyles.headerRow}>
{versionView && (
<div
className={classnames(
tableStyles.headerColumn,
tableStyles.headerColumnVersion
)}
>
&nbsp;
</div>
)}
{columns.map(({ key, label, className }) => {
const labelContent = isFunction(label) ? (
React.createElement(label)
Expand Down
14 changes: 12 additions & 2 deletions src/components/Executions/Tables/WorkflowVersionRow.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { makeStyles, Theme } from '@material-ui/core';
import Radio from '@material-ui/core/Radio';
import classnames from 'classnames';
import * as React from 'react';
import { ListRowProps } from 'react-virtualized';
Expand All @@ -11,14 +12,18 @@ import {

const useStyles = makeStyles((theme: Theme) => ({
row: {
paddingLeft: theme.spacing(2)
paddingLeft: theme.spacing(2),
cursor: 'pointer'
}
}));

export interface WorkflowVersionRowProps extends Partial<ListRowProps> {
columns: WorkflowVersionColumnDefinition[];
workflow: Workflow;
state: WorkflowExecutionsTableState;
onClick: (() => void) | undefined;
versionView?: boolean;
isChecked?: boolean;
}

/**
Expand All @@ -34,7 +39,10 @@ export const WorkflowVersionRow: React.FC<WorkflowVersionRowProps> = ({
columns,
workflow,
state,
style
style,
onClick,
versionView = false,
isChecked = false
}) => {
const tableStyles = useExecutionTableStyles();
const styles = useStyles();
Expand All @@ -47,8 +55,10 @@ export const WorkflowVersionRow: React.FC<WorkflowVersionRowProps> = ({
tableStyles.borderBottom
)}
style={style}
onClick={onClick}
>
<div className={tableStyles.rowColumns}>
{versionView && <Radio checked={isChecked} />}
{columns.map(({ className, key: columnKey, cellRenderer }) => (
<div
key={columnKey}
Expand Down
39 changes: 36 additions & 3 deletions src/components/Executions/Tables/WorkflowVersionsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,57 @@ import { useCommonStyles } from 'components/common/styles';
import { ListProps } from 'components/common/types';
import { DataList, DataListRef } from 'components/Tables/DataList';
import { Workflow } from 'models/Workflow/types';
import { Identifier } from 'models/Common/types';
import * as React from 'react';
import { useParams } from 'react-router';
import { history } from 'routes/history';
import { ListRowRenderer } from 'react-virtualized';
import { ExecutionsTableHeader } from './ExecutionsTableHeader';
import { useExecutionTableStyles } from './styles';
import { useWorkflowExecutionsTableState } from './useWorkflowExecutionTableState';
import { useWorkflowVersionsTableColumns } from './useWorkflowVersionsTableColumns';
import { WorkflowVersionRow } from './WorkflowVersionRow';
import { Routes } from 'routes/routes';

interface WorkflowVersionsTableProps extends ListProps<Workflow> {
versionView?: boolean;
}

interface WorkflowVersionRouteParams {
workflowVersion: string;
}

/**
* Renders a table of WorkflowVersion records.
* @param props
* @constructor
*/
export const WorkflowVersionsTable: React.FC<ListProps<Workflow>> = props => {
const { value: workflows } = props;
export const WorkflowVersionsTable: React.FC<WorkflowVersionsTableProps> = props => {
const { value: workflows, versionView } = props;
const state = useWorkflowExecutionsTableState();
const commonStyles = useCommonStyles();
const tableStyles = useExecutionTableStyles();
const listRef = React.useRef<DataListRef>(null);
const { workflowVersion } = useParams<WorkflowVersionRouteParams>();

const columns = useWorkflowVersionsTableColumns();

const retry = () => props.fetch();

const handleClickRow = React.useCallback(
({ project, name, domain, version }: Identifier) => () => {
history.push(
Routes.WorkflowVersionDetails.makeUrl(
project,
domain,
name,
version
)
);
},
[]
);

// Custom renderer to allow us to append error content to workflow versions which
// are in a failed state
const rowRenderer: ListRowRenderer = rowProps => {
Expand All @@ -38,6 +65,9 @@ export const WorkflowVersionsTable: React.FC<ListProps<Workflow>> = props => {
columns={columns}
workflow={workflow}
state={state}
onClick={versionView ? handleClickRow(workflow.id) : undefined}
versionView={versionView}
isChecked={workflowVersion === workflow.id.version}
/>
);
};
Expand All @@ -49,7 +79,10 @@ export const WorkflowVersionsTable: React.FC<ListProps<Workflow>> = props => {
commonStyles.flexFill
)}
>
<ExecutionsTableHeader columns={columns} />
<ExecutionsTableHeader
versionView={versionView}
columns={columns}
/>
<DataList
{...props}
onRetry={retry}
Expand Down
3 changes: 3 additions & 0 deletions src/components/Executions/Tables/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ export const useExecutionTableStyles = makeStyles((theme: Theme) => ({
marginLeft: theme.spacing(2)
}
},
headerColumnVersion: {
width: theme.spacing(4)
},
headerColumnName: {
fontSize: smallFontSize,
fontWeight: 'bold',
Expand Down
38 changes: 38 additions & 0 deletions src/components/Workflow/WorkflowVersionDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { withRouteParams } from 'components/common/withRouteParams';
import { EntityDetails } from 'components/Entities/EntityDetails';
import { ResourceIdentifier, ResourceType } from 'models/Common/types';
import * as React from 'react';

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

/**
* The view component for the Workflow Versions page
* @param projectId
* @param domainId
* @param workflowName
*/
export const WorkflowVersionDetailsContainer: React.FC<WorkflowVersionDetailsRouteParams> = ({
projectId,
domainId,
workflowName
}) => {
const id = React.useMemo<ResourceIdentifier>(
() => ({
resourceType: ResourceType.WORKFLOW,
project: projectId,
domain: domainId,
name: workflowName
}),
[projectId, domainId, workflowName]
);
return <EntityDetails id={id} versionView />;
};

export const WorkflowVersionDetails = withRouteParams<
WorkflowVersionDetailsRouteParams
>(WorkflowVersionDetailsContainer);
Loading