Skip to content

Commit

Permalink
feat: task list batch [DET-3224] (#780)
Browse files Browse the repository at this point in the history
  • Loading branch information
Caleb Hoyoul Kang authored Jun 29, 2020
1 parent d3f27c3 commit 2e10c64
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 12 deletions.
62 changes: 62 additions & 0 deletions webui/react/src/components/TableBatch.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
.base {
height: 0;
opacity: 0;
position: relative;
transition: height 0.2s ease-in-out, opacity 0.2s ease-in-out;

.container {
align-items: center;
background-color: var(--theme-colors-monochrome-17);
border: solid var(--theme-sizes-border-width) var(--theme-colors-monochrome-12);
border-radius: var(--theme-sizes-border-radius);
box-shadow: var(--theme-shadow);
display: flex;
height: 4.8rem;
justify-content: space-between;
padding: var(--theme-sizes-layout-medium);
padding-right: var(--theme-sizes-layout-big);
position: absolute;
width: 100%;
z-index: 2;
}
.container::before {
background-color: var(--theme-colors-monochrome-17);
border-color: var(--theme-colors-monochrome-12);
border-style: solid;
border-width: 0 var(--theme-sizes-border-width) var(--theme-sizes-border-width) 0;
box-shadow: var(--theme-shadow);
content: '';
display: block;
height: 1.2rem;
left: 3rem;
position: absolute;
top: 100%;
transform: translate(-50%, -50%) rotate(45deg);
width: 1.2rem;
}
.container::after {
background-color: var(--theme-colors-monochrome-17);
bottom: 0;
content: '';
height: calc(var(--theme-sizes-layout-medium) - 1);
left: 3rem;
position: absolute;
transform: translateX(-50%);
width: 3rem;
z-index: 1;
}
.actions {
align-items: center;
display: flex;
}
.actions > *:not(:last-child) {
margin-right: var(--theme-sizes-layout-medium);
}
.message {
font-size: var(--theme-sizes-font-medium);
}
&.show {
height: 4rem;
opacity: 1;
}
}
19 changes: 19 additions & 0 deletions webui/react/src/components/TableBatch.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Button } from 'antd';
import React from 'react';

import TableBatch from './TableBatch';

export default {
component: TableBatch,
title: 'TableBatch',
};

export const SampleTableBatch = (): React.ReactNode => {
return (
<TableBatch message="Apply batch operations to multiple items.">
<Button danger type="primary">Batch Operation 1</Button>
<Button>Batch Operation 2</Button>
<Button>Batch Operation 3</Button>
</TableBatch>
);
};
33 changes: 33 additions & 0 deletions webui/react/src/components/TableBatch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React, { PropsWithChildren } from 'react';

import css from './TableBatch.module.scss';

interface Props {
ids?: string[];
message: string;
show?: boolean;
}

const defaultProps = {
ids: [],
show: true,
};

const TableBatch: React.FC<Props> = (props: PropsWithChildren<Props>) => {
const classes = [ css.base ];

if (props.show) classes.push(css.show);

return (
<div className={classes.join(' ')}>
<div className={css.container}>
<div className={css.actions}>{props.children}</div>
<div className={css.message}>{props.message}</div>
</div>
</div>
);
};

TableBatch.defaultProps = defaultProps;

export default TableBatch;
80 changes: 68 additions & 12 deletions webui/react/src/components/TaskTable.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Table, Tooltip } from 'antd';
import { Button, notification, Table, Tooltip } from 'antd';
import { ColumnsType, ColumnType } from 'antd/lib/table';
import React, { MouseEventHandler } from 'react';
import axios from 'axios';
import React, { MouseEventHandler, useCallback, useMemo, useState } from 'react';

import { killCommand } from 'services/api';
import { AnyTask, CommandTask, CommandType, CommonProps } from 'types';
import { alphanumericSorter } from 'utils/data';
import { canBeOpened } from 'utils/task';
import { commandTypeToLabel } from 'utils/types';
import { commandTypeToLabel, isTaskKillable } from 'utils/types';

import Badge, { BadgeType } from './Badge';
import Icon from './Icon';
Expand All @@ -15,6 +17,7 @@ import {
actionsColumn, ellipsisRenderer, Renderer, startTimeColumn, stateColumn, userColumn,
} from './Table';
import css from './Table.module.scss';
import TableBatch from './TableBatch';

interface Props extends CommonProps {
tasks?: CommandTask[];
Expand Down Expand Up @@ -79,16 +82,69 @@ export const tableRowClickHandler = (record: AnyTask): {onClick?: MouseEventHand
});

const TaskTable: React.FC<Props> = ({ tasks }: Props) => {
const [ selectedRowKeys, setSelectedRowKeys ] = useState<string[]>([]);

const showBatch = selectedRowKeys.length !== 0;

const taskMap = useMemo(() => {
return (tasks || []).reduce((acc, task) => {
acc[task.id] = task;
return acc;
}, {} as Record<string, CommandTask>);
}, [ tasks ]);

const selectedTasks = useMemo(() => {
return selectedRowKeys.map(key => taskMap[key]);
}, [ selectedRowKeys, taskMap ]);

const hasKillable = useMemo(() => {
for (let i = 0; i < selectedTasks.length; i++) {
if (isTaskKillable(selectedTasks[i])) return true;
}
return false;
}, [ selectedTasks ]);

const handleBatchKill = useCallback(async () => {
try {
const source = axios.CancelToken.source();
const promises = selectedTasks.map(task => killCommand({
cancelToken: source.token,
commandId: task.id,
commandType: task.type,
}));
await Promise.all(promises);
} catch (e) {
notification.warn({
description: 'Please try again later.',
message: 'Unable to Kill Selected Tasks',
});
}
}, [ selectedTasks ]);
const handleTableRowSelect = useCallback(rowKeys => setSelectedRowKeys(rowKeys), []);

return (
<Table
className={css.base}
columns={columns}
dataSource={tasks}
loading={tasks === undefined}
rowClassName={(record): string => canBeOpened(record) ? linkCss.base : ''}
rowKey="id"
size="small"
onRow={tableRowClickHandler} />
<div>
<TableBatch message="Apply batch operations to multiple tasks." show={showBatch}>
<Button
danger
disabled={!hasKillable}
type="primary"
onClick={handleBatchKill}>Kill</Button>
</TableBatch>
<Table
className={css.base}
columns={columns}
dataSource={tasks}
loading={tasks === undefined}
rowClassName={(record): string => canBeOpened(record) ? linkCss.base : ''}
rowKey="id"
rowSelection={{
onChange: handleTableRowSelect,
selectedRowKeys,
}}
size="small"
onRow={tableRowClickHandler} />
</div>
);
};

Expand Down
7 changes: 7 additions & 0 deletions webui/react/src/styles/antd.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ html {
justify-content: center;
}

/* Checkbox */

.ant-checkbox-checked .ant-checkbox-inner::after {
border-left: 0;
border-top: 0;
}

/* Dropdown */

.ant-dropdown-menu {
Expand Down
1 change: 1 addition & 0 deletions webui/react/src/utils/task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,5 +125,6 @@ export const filterTasks = <T extends TaskType = TaskType, A extends AnyTask = A
matchesSearch<A>(task, search) &&
(!isExperiment || !(task as ExperimentTask).archived);
})
.filter(task => matchesSearch<A>(task, search))
.slice(0, filters.limit);
};

0 comments on commit 2e10c64

Please sign in to comment.