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(ui): Add pagination to workflow list. Fixes #1080 and #976 #2863

Merged
merged 6 commits into from
Apr 28, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
changes
  • Loading branch information
alexec committed Apr 27, 2020
commit 56d6ad58ae4663dbffe03e2deb264d1ecd0e9015
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {Page, SlidingPanel} from 'argo-ui';

import {Ticker} from 'argo-ui/src/index';
import * as classNames from 'classnames';
import {isNaN} from 'formik';
import * as moment from 'moment';
import * as React from 'react';
import {Link, RouteComponentProps} from 'react-router-dom';
Expand All @@ -11,19 +10,20 @@ import {Workflow} from '../../../../models';
import {uiUrl} from '../../../shared/base';
import {BasePage} from '../../../shared/components/base-page';
import {Loading} from '../../../shared/components/loading';
import {PaginationPanel} from '../../../shared/components/pagination-panel';
import {ResourceSubmit} from '../../../shared/components/resource-submit';
import {Timestamp} from '../../../shared/components/timestamp';
import {ZeroState} from '../../../shared/components/zero-state';
import {Consumer} from '../../../shared/context';
import {formatDuration} from '../../../shared/duration';
import {exampleWorkflow} from '../../../shared/examples';
import {Pagination, parseLimit} from '../../../shared/pagination';
import {services} from '../../../shared/services';
import {Utils} from '../../../shared/utils';
import {ArchivedWorkflowFilters} from '../archived-workflow-filters/archived-workflow-filters';

interface State {
offset: number;
nextOffset: number;
pagination: Pagination;
loading: boolean;
initialized: boolean;
managedNamespace: boolean;
Expand All @@ -45,8 +45,7 @@ export class ArchivedWorkflowList extends BasePage<RouteComponentProps<any>, Sta
super(props, context);
this.state = {
loading: true,
offset: this.parseOffset(this.queryParam('continue') || ''),
nextOffset: 0,
pagination: {offset: this.queryParam('offset') || '', limit: parseLimit(this.queryParam('limit'))},
initialized: false,
managedNamespace: false,
namespace: this.props.match.params.namespace || Utils.getCurrentNamespace() || '',
Expand All @@ -64,7 +63,7 @@ export class ArchivedWorkflowList extends BasePage<RouteComponentProps<any>, Sta
this.state.selectedLabels,
this.state.minStartedAt,
this.state.maxStartedAt,
this.state.offset
this.state.pagination
);
}

Expand Down Expand Up @@ -105,7 +104,7 @@ export class ArchivedWorkflowList extends BasePage<RouteComponentProps<any>, Sta
minStartedAt={this.state.minStartedAt}
maxStartedAt={this.state.maxStartedAt}
onChange={(namespace, selectedPhases, selectedLabels, minStartedAt, maxStartedAt) =>
this.changeFilters(namespace, selectedPhases, selectedLabels, minStartedAt, maxStartedAt, 0)
this.changeFilters(namespace, selectedPhases, selectedLabels, minStartedAt, maxStartedAt, this.state.pagination)
}
/>
</div>
Expand Down Expand Up @@ -149,15 +148,7 @@ export class ArchivedWorkflowList extends BasePage<RouteComponentProps<any>, Sta
}
}

private parseOffset(str: string) {
if (isNaN(str)) {
return 0;
}
const result = parseInt(str, 10);
return result >= 0 ? result : 0;
}

private changeFilters(namespace: string, selectedPhases: string[], selectedLabels: string[], minStartedAt: Date, maxStartedAt: Date, offset: number) {
private changeFilters(namespace: string, selectedPhases: string[], selectedLabels: string[], minStartedAt: Date, maxStartedAt: Date, pagination: Pagination) {
const params = new URLSearchParams();
selectedPhases.forEach(phase => {
params.append('phase', phase);
Expand All @@ -167,15 +158,16 @@ export class ArchivedWorkflowList extends BasePage<RouteComponentProps<any>, Sta
});
params.append('minStartedAt', minStartedAt.toISOString());
params.append('maxStartedAt', maxStartedAt.toISOString());
if (offset > 0) {
params.append('continue', offset.toString());
if (pagination.offset) {
params.append('offset', pagination.offset);
}
params.append('limit', pagination.limit.toString());
const url = 'archived-workflows/' + namespace + '?' + params.toString();
history.pushState(null, '', uiUrl(url));
this.fetchArchivedWorkflows(namespace, selectedPhases, selectedLabels, minStartedAt, maxStartedAt, offset && offset >= 0 ? offset : 0);
this.fetchArchivedWorkflows(namespace, selectedPhases, selectedLabels, minStartedAt, maxStartedAt, pagination);
}

private fetchArchivedWorkflows(namespace: string, selectedPhases: string[], selectedLabels: string[], minStartedAt: Date, maxStartedAt: Date, offset: number): void {
private fetchArchivedWorkflows(namespace: string, selectedPhases: string[], selectedLabels: string[], minStartedAt: Date, maxStartedAt: Date, pagination: Pagination): void {
let archivedWorkflowList;
let newNamespace = namespace;
if (!this.state.initialized) {
Expand All @@ -184,13 +176,13 @@ export class ArchivedWorkflowList extends BasePage<RouteComponentProps<any>, Sta
newNamespace = info.managedNamespace;
}
this.setState({initialized: true, managedNamespace: !!info.managedNamespace});
return services.archivedWorkflows.list(newNamespace, selectedPhases, selectedLabels, minStartedAt, maxStartedAt, offset);
return services.archivedWorkflows.list(newNamespace, selectedPhases, selectedLabels, minStartedAt, maxStartedAt, pagination);
});
} else {
if (this.state.managedNamespace) {
newNamespace = this.state.namespace;
}
archivedWorkflowList = services.archivedWorkflows.list(newNamespace, selectedPhases, selectedLabels, minStartedAt, maxStartedAt, offset);
archivedWorkflowList = services.archivedWorkflows.list(newNamespace, selectedPhases, selectedLabels, minStartedAt, maxStartedAt, pagination);
}
archivedWorkflowList
.then(list => {
Expand All @@ -201,14 +193,18 @@ export class ArchivedWorkflowList extends BasePage<RouteComponentProps<any>, Sta
selectedLabels,
minStartedAt,
maxStartedAt,
offset,
nextOffset: this.parseOffset(list.metadata.continue || ''),
pagination: {
limit: this.state.pagination.limit,
offset: this.state.pagination.offset,
nextOffset: list.metadata.continue
},
loading: false
});
Utils.setCurrentNamespace(newNamespace);
})
.catch(error => this.setState({error, loading: false}));
}

private renderWorkflows() {
if (!this.state.workflows) {
return <Loading />;
Expand All @@ -222,6 +218,7 @@ export class ArchivedWorkflowList extends BasePage<RouteComponentProps<any>, Sta
</ZeroState>
);
}

function wfDuration(workflow: models.WorkflowStatus, now: moment.Moment) {
const endTime = workflow.finishedAt ? moment(workflow.finishedAt) : now;
return endTime.diff(moment(workflow.startedAt)) / 1000;
Expand Down Expand Up @@ -253,33 +250,20 @@ export class ArchivedWorkflowList extends BasePage<RouteComponentProps<any>, Sta
</Link>
))}
</div>
<p>
{this.state.offset !== 0 && (
<button
className='argo-button argo-button--base-o'
onClick={() => {
this.changeFilters(this.state.namespace, this.state.selectedPhases, this.state.selectedLabels, this.state.minStartedAt, this.state.maxStartedAt, 0);
}}>
<i className='fa fa-chevron-left' /> Start
</button>
)}
{this.state.nextOffset !== 0 && (
<button
className='argo-button argo-button--base-o'
onClick={() => {
this.changeFilters(
this.state.namespace,
this.state.selectedPhases,
this.state.selectedLabels,
this.state.minStartedAt,
this.state.maxStartedAt,
this.state.nextOffset
);
}}>
Next: {this.state.nextOffset} <i className='fa fa-chevron-right' />
</button>
)}
</p>
<PaginationPanel
onChange={pagination => {
this.setState({pagination});
this.changeFilters(
this.state.namespace,
this.state.selectedPhases,
this.state.selectedLabels,
this.state.minStartedAt,
this.state.maxStartedAt,
pagination
);
}}
pagination={this.state.pagination}
/>
<p>
<i className='fa fa-info-circle' /> Records are created in the archive when a workflow completes. {learnMore}.
</p>
Expand Down
46 changes: 46 additions & 0 deletions ui/src/app/shared/components/pagination-panel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import * as React from 'react';
import {Pagination} from '../pagination';

export class PaginationPanel extends React.Component<{pagination: Pagination; onChange: (pagination: Pagination) => void}> {
public render() {
return (
<p>
<button
disabled={!this.props.pagination.offset}
className='argo-button argo-button--base-o'
onClick={() => this.props.onChange({limit: this.props.pagination.limit})}>
First page
</button>
<button
disabled={!this.props.pagination.nextOffset}
className='argo-button argo-button--base-o'
onClick={() =>
this.props.onChange({
limit: this.props.pagination.limit,
offset: this.props.pagination.nextOffset
})
}>
Next page <i className='fa fa-chevron-right' />
</button>
<small className='fa-pull-right'>
<select
className='small'
onChange={e =>
this.props.onChange({
offset: this.props.pagination.offset,
limit: parseInt(e.target.value)
})
}
value={this.props.pagination.limit}>
{[5, 10, 20, 50, 100, 500].map(limit => (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

<option key={limit} value={limit}>
{limit}
</option>
))}
</select>{' '}
results per page
</small>
</p>
);
}
}
12 changes: 12 additions & 0 deletions ui/src/app/shared/pagination.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {isNaN} from 'formik';

export interface Pagination {
offset?: string;
limit: number;
nextOffset?: string;
}

export function parseLimit(str: string) {
const v = parseInt(str);
return isNaN(v) ? 10 : v;
}
12 changes: 7 additions & 5 deletions ui/src/app/shared/services/archived-workflows-service.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import * as models from '../../../models';
import {Pagination} from '../pagination';
import requests from './requests';

export class ArchivedWorkflowsService {
public list(namespace: string, phases: string[], labels: string[], minStartedAt: Date, maxStartedAt: Date, offset: number) {
public list(namespace: string, phases: string[], labels: string[], minStartedAt: Date, maxStartedAt: Date, pagination: Pagination) {
return requests
.get(`api/v1/archived-workflows?${this.queryParams({namespace, phases, labels, minStartedAt, maxStartedAt, offset}).join('&')}`)
.get(`api/v1/archived-workflows?limit=${this.queryParams({namespace, phases, labels, minStartedAt, maxStartedAt, pagination}).join('&')}`)
.then(res => res.body as models.WorkflowList);
}

Expand All @@ -16,7 +17,7 @@ export class ArchivedWorkflowsService {
return requests.delete(`api/v1/archived-workflows/${uid}`);
}

private queryParams(filter: {namespace?: string; phases?: Array<string>; labels?: Array<string>; minStartedAt?: Date; maxStartedAt?: Date; offset?: number}) {
private queryParams(filter: {namespace?: string; phases?: Array<string>; labels?: Array<string>; minStartedAt?: Date; maxStartedAt?: Date; pagination: Pagination}) {
const queryParams: string[] = [];
const fieldSelector = this.fieldSelectorParams(filter.namespace, filter.minStartedAt, filter.maxStartedAt);
if (fieldSelector.length > 0) {
Expand All @@ -26,9 +27,10 @@ export class ArchivedWorkflowsService {
if (labelSelector.length > 0) {
queryParams.push(`listOptions.labelSelector=${labelSelector}`);
}
if (filter.offset) {
queryParams.push(`listOptions.continue=${filter.offset}`);
if (filter.pagination.offset) {
queryParams.push(`listOptions.continue=${filter.pagination.offset}`);
}
queryParams.push(`listOptions.limit=${filter.pagination.limit}`);
return queryParams;
}

Expand Down
12 changes: 8 additions & 4 deletions ui/src/app/shared/services/workflows-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {Observable, Observer} from 'rxjs';
import {catchError, map} from 'rxjs/operators';
import * as models from '../../../models';
import {Workflow, WorkflowList} from '../../../models';
import {Pagination} from '../pagination';
import requests from './requests';
import {WorkflowDeleteResponse} from './responses';

Expand All @@ -16,10 +17,13 @@ export class WorkflowsService {
.then(res => res.body as Workflow);
}

public list(namespace: string, phases: string[], labels: string[], offset: string) {
return requests
.get(`api/v1/workflows/${namespace}?${fieldsFilter}&listOptions.continue=${offset}${this.queryParams({phases, labels}).join('&')}`)
.then(res => res.body as WorkflowList);
public list(namespace: string, phases: string[], labels: string[], pagination: Pagination) {
const params = this.queryParams({phases, labels});
if (pagination.offset) {
params.push(`listOptions.continue=${pagination.offset}`);
}
params.push(`listOptions.limit=${pagination.limit}`);
return requests.get(`api/v1/workflows/${namespace}?${fieldsFilter}&${params.join('&')}`).then(res => res.body as WorkflowList);
}

public get(namespace: string, name: string) {
Expand Down
Loading