Adds sparkline to templates list

This commit is contained in:
mabashian 2019-08-06 15:51:11 -04:00
parent dbcc3c5733
commit 19d2c8c634
10 changed files with 183 additions and 6 deletions

View File

@ -353,7 +353,7 @@ function projectsListController (
};
function buildTooltips (project) {
project.statusIcon = getStatusIcon(project);
project.statusIcon = getJobStatusIcon(project);
project.statusTip = getStatusTooltip(project);
project.scm_update_tooltip = vm.strings.get('update.GET_LATEST');
project.scm_update_disabled = false;
@ -408,7 +408,7 @@ function projectsListController (
};
}
function getStatusIcon (project) {
function getJobStatusIcon (project) {
let icon = 'none';
switch (project.status) {
case 'n/a':

View File

@ -0,0 +1,128 @@
import React, { Fragment } from 'react';
import { node, object, string } from 'prop-types';
import { Link } from 'react-router-dom';
import styled, { keyframes } from 'styled-components';
import { Tooltip } from '@patternfly/react-core';
const Pulse = keyframes`
from {
-webkit-transform:scale(1);
}
to {
-webkit-transform:scale(0);
}
`;
const Wrapper = styled.div`
width: 14px;
height: 14px;
`;
const RunningJob = styled(Wrapper)`
background-color: #5cb85c;
padding-right: 0px;
text-shadow:
-1px -1px 0 #ffffff,
1px -1px 0 #ffffff,
-1px 1px 0 #ffffff,
1px 1px 0 #ffffff;
animation: ${Pulse} 1.5s linear infinite alternate;
`;
const WaitingJob = styled(Wrapper)`
border: 1px solid #d7d7d7;
`;
const FinishedJob = styled(Wrapper)`
flex: 0 1 auto;
> * {
width: 14px;
height: 7px;
}
`;
const SuccessfulTop = styled.div`
background-color: #5cb85c;
`;
const SuccessfulBottom = styled.div`
border: 1px solid #b7b7b7;
border-top: 0;
background: #ffffff;
`;
const FailedTop = styled.div`
border: 1px solid #b7b7b7;
border-bottom: 0;
background: #ffffff;
`;
const FailedBottom = styled.div`
background-color: #D9534F;
`;
const JobStatusIcon = ({ job, link, tooltip, ...props }) => {
let Icon = (
<Fragment>
{ job.status === 'running' && <RunningJob /> }
{ (job.status === 'new'
|| job.status === 'pending'
|| job.status === 'waiting')
&& <WaitingJob />
}
{ (job.status === 'failed'
|| job.status === 'error'
|| job.status === 'canceled')
&& (
<FinishedJob>
<FailedTop />
<FailedBottom />
</FinishedJob>
)
}
{ job.status === 'successful' && (
<FinishedJob>
<SuccessfulTop />
<SuccessfulBottom />
</FinishedJob>
)}
</Fragment>
);
if (link) {
Icon = (
<Link to={link}>
{Icon}
</Link>
);
}
if (tooltip) {
return (
<div {...props}>
<Tooltip position="top" content={tooltip}>
{Icon}
</Tooltip>
</div>
);
}
return (
<div {...props}>
{Icon}
</div>
);
}
JobStatusIcon.propTypes = {
job: object.isRequired,
link: string,
tooltip: node
};
JobStatusIcon.defaultProps = {
link: null,
tooltip: null,
};
export default JobStatusIcon;

View File

@ -0,0 +1,41 @@
import React, { Fragment } from 'react';
import { arrayOf, object } from 'prop-types';
import { withI18n } from '@lingui/react';
import { JobStatusIcon as _JobStatusIcon } from '@components/Sparkline';
import styled from 'styled-components';
import { t } from '@lingui/macro';
import { JOB_TYPE_URL_SEGMENTS } from '../../constants';
const JobStatusIcon = styled(props => <_JobStatusIcon {...props} />)`
margin-right: 5px;
`;
const Sparkline = ({ i18n, jobs }) => {
const generateTooltip = (job) => (
<Fragment>
<div>{i18n._(t`JOB ID:`)} {job.id}</div>
<div>{i18n._(t`STATUS:`)} {job.status.toUpperCase()}</div>
<div>{i18n._(t`FINISHED:`)} {job.finished}</div>
</Fragment>
);
return (
(jobs.map(job => (
<JobStatusIcon
key={job.id}
job={job}
link={`/jobs/${JOB_TYPE_URL_SEGMENTS[job.type]}/${job.id}`}
tooltip={generateTooltip(job)}
/>
)))
)
};
Sparkline.propTypes = {
jobs: arrayOf(object),
};
Sparkline.defaultProps = {
jobs: [],
};
export default withI18n()(Sparkline);

View File

@ -0,0 +1,2 @@
export { default as Sparkline } from './Sparkline';
export { default as JobStatusIcon } from './JobStatusIcon';

View File

@ -15,7 +15,7 @@ import RoutedTabs from '@components/RoutedTabs';
import JobDetail from './JobDetail';
import JobOutput from './JobOutput';
import { JOB_TYPE_URL_SEGMENTS } from './constants';
import { JOB_TYPE_URL_SEGMENTS } from '../../constants';
class Job extends Component {
constructor(props) {

View File

@ -9,7 +9,7 @@ import {
import DataListCell from '@components/DataListCell';
import VerticalSeparator from '@components/VerticalSeparator';
import { toTitleCase } from '@util/strings';
import { JOB_TYPE_URL_SEGMENTS } from '../constants';
import { JOB_TYPE_URL_SEGMENTS } from '../../../constants';
class JobListItem extends Component {
render() {

View File

@ -1,7 +1,7 @@
import React, { Component } from 'react';
import { Redirect } from 'react-router-dom';
import { UnifiedJobsAPI } from '@api';
import { JOB_TYPE_URL_SEGMENTS } from './constants';
import { JOB_TYPE_URL_SEGMENTS } from '../../constants';
class JobTypeRedirect extends Component {
static defaultProps = {

View File

@ -6,7 +6,7 @@ import Breadcrumbs from '@components/Breadcrumbs/Breadcrumbs';
import Job from './Job';
import JobTypeRedirect from './JobTypeRedirect';
import JobList from './JobList/JobList';
import { JOB_TYPE_URL_SEGMENTS } from './constants';
import { JOB_TYPE_URL_SEGMENTS } from '../../constants';
class Jobs extends Component {
constructor(props) {

View File

@ -16,6 +16,7 @@ import styled from 'styled-components';
import DataListCell from '@components/DataListCell';
import LaunchButton from '@components/LaunchButton';
import VerticalSeparator from '@components/VerticalSeparator';
import { Sparkline } from '@components/Sparkline';
import { toTitleCase } from '@util/strings';
const StyledButton = styled(PFButton)`
@ -56,6 +57,11 @@ class TemplateListItem extends Component {
<DataListCell key="type">
{toTitleCase(template.type)}
</DataListCell>,
<DataListCell key="sparkline">
<Sparkline
jobs={template.summary_fields.recent_jobs}
/>
</DataListCell>,
<DataListCell lastcolumn="true" key="launch">
{canLaunch && template.type === 'job_template' && (
<Tooltip content={i18n._(t`Launch`)} position="top">