-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8402 from jlmitch5/dashboardAug20
add ui_next dashboard Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
- Loading branch information
Showing
19 changed files
with
2,126 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import Base from '../Base'; | ||
|
||
class Dashboard extends Base { | ||
constructor(http) { | ||
super(http); | ||
this.baseUrl = '/api/v2/dashboard/'; | ||
} | ||
|
||
readJobGraph(params) { | ||
return this.http.get(`${this.baseUrl}graphs/jobs/`, { | ||
params, | ||
}); | ||
} | ||
} | ||
|
||
export default Dashboard; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,253 @@ | ||
import React, { Component, Fragment } from 'react'; | ||
import React, { Fragment, useCallback, useEffect, useState } from 'react'; | ||
import styled from 'styled-components'; | ||
import { withI18n } from '@lingui/react'; | ||
import { t } from '@lingui/macro'; | ||
import { | ||
Card, | ||
CardHeader, | ||
CardActions, | ||
CardBody, | ||
PageSection, | ||
PageSectionVariants, | ||
Select, | ||
SelectVariant, | ||
SelectOption, | ||
Tabs, | ||
Tab, | ||
TabTitleText, | ||
Title, | ||
} from '@patternfly/react-core'; | ||
|
||
class Dashboard extends Component { | ||
render() { | ||
const { i18n } = this.props; | ||
const { light } = PageSectionVariants; | ||
|
||
return ( | ||
<Fragment> | ||
<PageSection variant={light} className="pf-m-condensed"> | ||
<Title size="2xl" headingLevel="h2"> | ||
{i18n._(t`Dashboard`)} | ||
</Title> | ||
</PageSection> | ||
<PageSection /> | ||
</Fragment> | ||
); | ||
import useRequest from '../../util/useRequest'; | ||
import { DashboardAPI } from '../../api'; | ||
import JobList from '../../components/JobList'; | ||
|
||
import LineChart from './shared/LineChart'; | ||
import Count from './shared/Count'; | ||
import DashboardTemplateList from './shared/DashboardTemplateList'; | ||
|
||
const Counts = styled.div` | ||
display: grid; | ||
grid-template-columns: repeat(6, 1fr); | ||
grid-gap: var(--pf-global--spacer--lg); | ||
@media (max-width: 900px) { | ||
grid-template-columns: repeat(3, 1fr); | ||
grid-auto-rows: 1fr; | ||
} | ||
`; | ||
|
||
const MainPageSection = styled(PageSection)` | ||
padding-top: 0; | ||
padding-bottom: 0; | ||
& .spacer { | ||
margin-bottom: var(--pf-global--spacer--lg); | ||
} | ||
`; | ||
|
||
const GraphCardHeader = styled(CardHeader)` | ||
margin-top: var(--pf-global--spacer--lg); | ||
`; | ||
|
||
const GraphCardActions = styled(CardActions)` | ||
margin-left: initial; | ||
padding-left: 0; | ||
`; | ||
|
||
function Dashboard({ i18n }) { | ||
const { light } = PageSectionVariants; | ||
|
||
const [isPeriodDropdownOpen, setIsPeriodDropdownOpen] = useState(false); | ||
const [isJobTypeDropdownOpen, setIsJobTypeDropdownOpen] = useState(false); | ||
const [periodSelection, setPeriodSelection] = useState('month'); | ||
const [jobTypeSelection, setJobTypeSelection] = useState('all'); | ||
const [activeTabId, setActiveTabId] = useState(0); | ||
|
||
const { | ||
result: { jobGraphData, countData }, | ||
request: fetchDashboardGraph, | ||
} = useRequest( | ||
useCallback(async () => { | ||
const [{ data }, { data: dataFromCount }] = await Promise.all([ | ||
DashboardAPI.readJobGraph({ | ||
period: periodSelection, | ||
job_type: jobTypeSelection, | ||
}), | ||
DashboardAPI.read(), | ||
]); | ||
const newData = {}; | ||
data.jobs.successful.forEach(([dateSecs, count]) => { | ||
if (!newData[dateSecs]) { | ||
newData[dateSecs] = {}; | ||
} | ||
newData[dateSecs].successful = count; | ||
}); | ||
data.jobs.failed.forEach(([dateSecs, count]) => { | ||
if (!newData[dateSecs]) { | ||
newData[dateSecs] = {}; | ||
} | ||
newData[dateSecs].failed = count; | ||
}); | ||
const jobData = Object.keys(newData).map(dateSecs => { | ||
const [created] = new Date(dateSecs * 1000).toISOString().split('T'); | ||
newData[dateSecs].created = created; | ||
return newData[dateSecs]; | ||
}); | ||
return { | ||
jobGraphData: jobData, | ||
countData: dataFromCount, | ||
}; | ||
}, [periodSelection, jobTypeSelection]), | ||
{ | ||
jobGraphData: [], | ||
countData: {}, | ||
} | ||
); | ||
|
||
useEffect(() => { | ||
fetchDashboardGraph(); | ||
}, [fetchDashboardGraph, periodSelection, jobTypeSelection]); | ||
|
||
return ( | ||
<Fragment> | ||
<PageSection variant={light} className="pf-m-condensed"> | ||
<Title size="2xl" headingLevel="h2"> | ||
{i18n._(t`Dashboard`)} | ||
</Title> | ||
</PageSection> | ||
<PageSection> | ||
<Counts> | ||
<Count | ||
link="/hosts" | ||
data={countData?.hosts?.total} | ||
label={i18n._(t`Hosts`)} | ||
/> | ||
<Count | ||
failed | ||
link="/hosts?host.last_job_host_summary__failed=true" | ||
data={countData?.hosts?.failed} | ||
label={i18n._(t`Failed hosts`)} | ||
/> | ||
<Count | ||
link="/inventories" | ||
data={countData?.inventories?.total} | ||
label={i18n._(t`Inventories`)} | ||
/> | ||
<Count | ||
failed | ||
link="/inventories?inventory.inventory_sources_with_failures__gt=0" | ||
data={countData?.inventories?.failed} | ||
label={i18n._(t`Inventory sync failures`)} | ||
/> | ||
<Count | ||
link="/projects" | ||
data={countData?.projects?.total} | ||
label={i18n._(t`Projects`)} | ||
/> | ||
<Count | ||
failed | ||
link="/projects?project.status__in=failed,canceled" | ||
data={countData?.projects?.failed} | ||
label={i18n._(t`Project sync failures`)} | ||
/> | ||
</Counts> | ||
</PageSection> | ||
<MainPageSection> | ||
<div className="spacer"> | ||
<Card id="dashboard-main-container"> | ||
<Tabs | ||
aria-label={i18n._(t`Tabs`)} | ||
activeKey={activeTabId} | ||
onSelect={(key, eventKey) => setActiveTabId(eventKey)} | ||
> | ||
<Tab | ||
aria-label={i18n._(t`Job status graph tab`)} | ||
eventKey={0} | ||
title={<TabTitleText>{i18n._(t`Job status`)}</TabTitleText>} | ||
/> | ||
<Tab | ||
aria-label={i18n._(t`Recent Jobs list tab`)} | ||
eventKey={1} | ||
title={<TabTitleText>{i18n._(t`Recent Jobs`)}</TabTitleText>} | ||
/> | ||
<Tab | ||
aria-label={i18n._(t`Recent Templates list tab`)} | ||
eventKey={2} | ||
title={ | ||
<TabTitleText>{i18n._(t`Recent Templates`)}</TabTitleText> | ||
} | ||
/> | ||
</Tabs> | ||
{activeTabId === 0 && ( | ||
<Fragment> | ||
<GraphCardHeader> | ||
<GraphCardActions> | ||
<Select | ||
variant={SelectVariant.single} | ||
placeholderText={i18n._(t`Select period`)} | ||
aria-label={i18n._(t`Select period`)} | ||
className="periodSelect" | ||
onToggle={setIsPeriodDropdownOpen} | ||
onSelect={(event, selection) => | ||
setPeriodSelection(selection) | ||
} | ||
selections={periodSelection} | ||
isOpen={isPeriodDropdownOpen} | ||
> | ||
<SelectOption key="month" value="month"> | ||
{i18n._(t`Past month`)} | ||
</SelectOption> | ||
<SelectOption key="two_weeks" value="two_weeks"> | ||
{i18n._(t`Past two weeks`)} | ||
</SelectOption> | ||
<SelectOption key="week" value="week"> | ||
{i18n._(t`Past week`)} | ||
</SelectOption> | ||
</Select> | ||
<Select | ||
variant={SelectVariant.single} | ||
placeholderText={i18n._(t`Select job type`)} | ||
aria-label={i18n._(t`Select job type`)} | ||
className="jobTypeSelect" | ||
onToggle={setIsJobTypeDropdownOpen} | ||
onSelect={(event, selection) => | ||
setJobTypeSelection(selection) | ||
} | ||
selections={jobTypeSelection} | ||
isOpen={isJobTypeDropdownOpen} | ||
> | ||
<SelectOption key="all" value="all"> | ||
{i18n._(t`All job types`)} | ||
</SelectOption> | ||
<SelectOption key="inv_sync" value="inv_sync"> | ||
{i18n._(t`Inventory sync`)} | ||
</SelectOption> | ||
<SelectOption key="scm_update" value="scm_update"> | ||
{i18n._(t`SCM update`)} | ||
</SelectOption> | ||
<SelectOption key="playbook_run" value="playbook_run"> | ||
{i18n._(t`Playbook run`)} | ||
</SelectOption> | ||
</Select> | ||
</GraphCardActions> | ||
</GraphCardHeader> | ||
<CardBody> | ||
<LineChart | ||
height={390} | ||
id="d3-line-chart-root" | ||
data={jobGraphData} | ||
/> | ||
</CardBody> | ||
</Fragment> | ||
)} | ||
{activeTabId === 1 && <JobList defaultParams={{ page_size: 5 }} />} | ||
{activeTabId === 2 && <DashboardTemplateList />} | ||
</Card> | ||
</div> | ||
</MainPageSection> | ||
</Fragment> | ||
); | ||
} | ||
|
||
export default withI18n()(Dashboard); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.