Skip to content

Commit

Permalink
Feat/version details (flyteorg#198)
Browse files Browse the repository at this point in the history
* feat: add workflow versions table

Signed-off-by: csirius <[email protected]>

* feat: workflow version details page

Signed-off-by: csirius <[email protected]>
  • Loading branch information
govalt authored Sep 16, 2021
1 parent 99bace4 commit 9920e0a
Show file tree
Hide file tree
Showing 10 changed files with 198 additions and 36 deletions.
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

0 comments on commit 9920e0a

Please sign in to comment.