1
0
mirror of https://github.com/ansible/awx.git synced 2024-10-31 15:21:13 +03:00

build basic job details

This commit is contained in:
Keith Grant 2019-06-25 15:31:14 -07:00
parent 41b0367627
commit 4372e977f0
6 changed files with 158 additions and 29 deletions

View File

@ -5,6 +5,11 @@ class Jobs extends Base {
super(http);
this.baseUrl = '/api/v2/jobs/';
}
readDetail (id, type) {
// TODO: adjust url based on type
return this.http.get(`${this.baseUrl}${id}/`);
}
}
export default Jobs;

View File

@ -26,7 +26,7 @@ const DetailValue = styled(({ fullWidth, ...props }) => (
`;
const Detail = ({ label, value, fullWidth }) => {
if (!value) return null;
if (!value && typeof value !== 'number') { return null; }
return (
<Fragment>
<DetailName component={TextListItemVariants.dt} fullWidth={fullWidth}>

View File

@ -49,7 +49,7 @@ class Job extends Component {
this.setState({ contentError: null, hasContentLoading: true });
try {
const { data } = await JobsAPI.readDetail(id);
const { data } = await JobsAPI.readDetail(id, match.params.type);
setBreadcrumb(data);
this.setState({ job: data });
} catch (err) {
@ -106,17 +106,31 @@ class Job extends Component {
<Card>
{cardHeader}
<Switch>
<Redirect from="/jobs/:id" to="/jobs/:id/details" exact />
<Redirect
from="/jobs/:type/:id"
to="/jobs/:type/:id/details"
exact
/>
{job && (
<Route
path="/jobs/:id/details"
render={() => <JobDetail match={match} job={job} />}
path="/jobs/:type/:id/details"
render={() => (
<JobDetail
match={match}
job={job}
/>
)}
/>
)}
{job && (
<Route
path="/jobs/:id/output"
render={() => <JobOutput match={match} job={job} />}
path="/jobs/:type/:id/output"
render={() => (
<JobOutput
match={match}
job={job}
/>
)}
/>
)}
</Switch>

View File

@ -1,35 +1,145 @@
import React, { Component } from 'react';
import React, { Component, useState, useEffect } from 'react';
import { Link, withRouter } from 'react-router-dom';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { CardBody, Button } from '@patternfly/react-core';
import styled from 'styled-components';
import { DetailList, Detail } from '@components/DetailList';
import { ChipGroup, Chip } from '@components/Chip';
import ContentError from '@components/ContentError';
import ContentLoading from '@components/ContentLoading';
import { toTitleCase } from '@util/strings';
const ActionButtonWrapper = styled.div`
display: flex;
justify-content: flex-end;
`;
class JobDetail extends Component {
render() {
const { job, i18n } = this.props;
return (
<CardBody>
<b>{job.name}</b>
const VERBOSITY = {
0: '0 (Normal)',
1: '1 (Verbose)',
2: '2 (More Verbose)',
3: '3 (Debug)',
4: '4 (Connection Debug)',
};
<ActionButtonWrapper>
<Button
variant="secondary"
aria-label="close"
component={Link}
to="/jobs"
>
{i18n._(t`Close`)}
</Button>
</ActionButtonWrapper>
</CardBody>
);
}
function JobDetail ({ job, i18n }) {
const [instanceGroups, setInstanceGroups] = useState(null);
console.log(job);
const {
job_template: jobTemplate,
project,
inventory,
instance_group: instanceGroup,
} = job.summary_fields;
return (
<CardBody>
<DetailList>
{/* TODO: add status icon? */}
<Detail
label={i18n._(t`Status`)}
value={toTitleCase(job.status)}
/>
<Detail
label={i18n._(t`Started`)}
value={job.started}
/>
<Detail
label={i18n._(t`Finished`)}
value={job.finished}
/>
<Detail
label={i18n._(t`Template`)}
value={(
<Link to={`/templates/job_template/${jobTemplate.id}`}>
{jobTemplate.name}
</Link>
)}
/>
<Detail
label={i18n._(t`Job Type`)}
value={toTitleCase(job.job_type)}
/>
<Detail
label={i18n._(t`Inventory`)}
value={(
<Link to={`/inventory/${inventory.id}`}>
{inventory.name}
</Link>
)}
/>
{/* TODO: show project status icon */}
<Detail
label={i18n._(t`Project`)}
value={(
<Link to={`/projects/${project.id}`}>
{project.name}
</Link>
)}
/>
<Detail
label={i18n._(t`Revision`)}
value={job.scm_revision}
/>
<Detail
label={i18n._(t`Playbook`)}
value={null}
/>
<Detail
label={i18n._(t`Limit`)}
value={job.limit}
/>
<Detail
label={i18n._(t`Verbosity`)}
value={VERBOSITY[job.verbosity]}
/>
<Detail
label={i18n._(t`Environment`)}
value={null}
/>
<Detail
label={i18n._(t`Execution Node`)}
value={job.exucution_node}
/>
<Detail
label={i18n._(t`Instance Group`)}
value={(
<Link to={`/instance_groups/${instanceGroup.id}`}>
{instanceGroup.name}
</Link>
)}
/>
<Detail
label={i18n._(t`Job Slice`)}
value={`${job.job_slice_number}/${job.job_slice_count}`}
/>
{(instanceGroups && instanceGroups.length > 0) && (
<Detail
fullWidth
label={i18n._(t`Instance Groups`)}
value={(
<ChipGroup showOverflowAfter={5}>
{instanceGroups.map(ig => (
<Chip key={ig.id} isReadOnly>{ig.name}</Chip>
))}
</ChipGroup>
)}
/>
)}
</DetailList>
<ActionButtonWrapper>
<Button
variant="secondary"
aria-label="close"
component={Link}
to="/jobs"
>
{i18n._(t`Close`)}
</Button>
</ActionButtonWrapper>
</CardBody>
);
}
export default withI18n()(withRouter(JobDetail));

View File

@ -32,7 +32,7 @@ class JobListItem extends Component {
<DataListCell key="divider">
<VerticalSeparator />
<span>
<Link to={`/jobs/${job.id}`}>
<Link to={`/jobs/${job.type}/${job.id}`}>
<b>{job.name}</b>
</Link>
</span>

View File

@ -58,7 +58,7 @@ class Jobs extends Component {
)}
/>
<Route
path={`${match.path}/:id`}
path={`${match.path}/:type/:id`}
render={() => (
<Job
history={history}