Migrate to patternfly 4
Change-Id: Ieb31bc739740c382d46a074f15ce7e9e94c1b480
This commit is contained in:
parent
3aa04800ef
commit
d42b04f049
2184
package-lock.json
generated
2184
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -2,17 +2,18 @@
|
||||
"name": "ara-web",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"homepage": "./",
|
||||
"dependencies": {
|
||||
"@patternfly/patternfly-next": "1.0.105",
|
||||
"axios": "^0.18.0",
|
||||
"patternfly": "^3.54.2",
|
||||
"patternfly-react": "^2.17.3",
|
||||
"react": "^16.5.1",
|
||||
"react-dom": "^16.5.1",
|
||||
"react-redux": "^5.0.7",
|
||||
"react-router-dom": "^4.3.1",
|
||||
"react-scripts": "^1.1.5",
|
||||
"redux": "^4.0.0",
|
||||
"redux-thunk": "^2.3.0"
|
||||
"redux-thunk": "^2.3.0",
|
||||
"styled-components": "^4.1.3"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
|
@ -1,3 +0,0 @@
|
||||
body {
|
||||
background-color: #f5f5f5;
|
||||
}
|
57
src/App.js
57
src/App.js
@ -2,57 +2,42 @@ import React, { Component } from "react";
|
||||
import { BrowserRouter, Route, Switch, Redirect } from "react-router-dom";
|
||||
import { Provider } from "react-redux";
|
||||
|
||||
import "./App.css";
|
||||
import "@patternfly/patternfly-next/patternfly.css";
|
||||
import store from "./store";
|
||||
import { setConfig } from "./config/configActions";
|
||||
import * as Containers from "./containers";
|
||||
|
||||
class App extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
loading: true
|
||||
};
|
||||
}
|
||||
state = {
|
||||
isLoading: true
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
store.dispatch(
|
||||
setConfig({
|
||||
apiURL: "http://localhost:8000",
|
||||
ara_version: "1.0.0",
|
||||
ansible_version: "2.6",
|
||||
python_version: "3.6"
|
||||
apiURL: "http://localhost:8000"
|
||||
})
|
||||
);
|
||||
this.setState({ loading: false });
|
||||
this.setState({ isLoading: false });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isLoading } = this.state;
|
||||
if (isLoading) return null;
|
||||
return (
|
||||
<div className="App">
|
||||
{this.state.loading ? (
|
||||
<Containers.LoadingContainer />
|
||||
) : (
|
||||
<Provider store={store}>
|
||||
<BrowserRouter>
|
||||
<Switch>
|
||||
<Redirect from="/" exact to="/playbooks" />
|
||||
<Route
|
||||
path="/playbooks"
|
||||
exact
|
||||
component={Containers.PlaybooksContainer}
|
||||
/>
|
||||
<Route
|
||||
path="/about"
|
||||
exact
|
||||
component={Containers.AboutContainer}
|
||||
/>
|
||||
<Route component={Containers.Container404} />
|
||||
</Switch>
|
||||
</BrowserRouter>
|
||||
</Provider>
|
||||
)}
|
||||
</div>
|
||||
<Provider store={store}>
|
||||
<BrowserRouter>
|
||||
<Switch>
|
||||
<Redirect from="/" exact to="/playbooks" />
|
||||
<Route
|
||||
path="/playbooks"
|
||||
exact
|
||||
component={Containers.PlaybooksContainer}
|
||||
/>
|
||||
<Route component={Containers.Container404} />
|
||||
</Switch>
|
||||
</BrowserRouter>
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +0,0 @@
|
||||
import React, { Component } from "react";
|
||||
import { MainContainer } from "../containers";
|
||||
|
||||
export default class AboutContainer extends Component {
|
||||
render() {
|
||||
return (
|
||||
<MainContainer>
|
||||
<p>AboutContainer</p>
|
||||
</MainContainer>
|
||||
);
|
||||
}
|
||||
}
|
@ -1,5 +1,3 @@
|
||||
export { default as Container404 } from "./layout/Container404";
|
||||
export { default as LoadingContainer } from "./layout/LoadingContainer";
|
||||
export { default as MainContainer } from "./layout/MainContainer";
|
||||
export { default as PlaybooksContainer } from "./playbooks/PlaybooksContainer";
|
||||
export { default as AboutContainer } from "./about/AboutContainer";
|
||||
|
@ -1,5 +0,0 @@
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: sans-serif;
|
||||
}
|
@ -1,8 +1,5 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import "patternfly/dist/css/patternfly.min.css";
|
||||
import "patternfly/dist/css/patternfly-additions.min.css";
|
||||
import "./index.css";
|
||||
import App from "./App";
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById("root"));
|
||||
|
@ -5,7 +5,15 @@ export default class Container404 extends Component {
|
||||
render() {
|
||||
return (
|
||||
<MainContainer>
|
||||
<p>404</p>
|
||||
<div className="pf-l-bullseye">
|
||||
<div className="pf-l-bullseye__item">
|
||||
<div className="pf-c-card">
|
||||
<div className="pf-c-card__body">
|
||||
<p>We are looking for your page...but we can't find it</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</MainContainer>
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +0,0 @@
|
||||
import React, { Component } from "react";
|
||||
|
||||
export default class LoadingContainer extends Component {
|
||||
render() {
|
||||
return <div>loading...</div>;
|
||||
}
|
||||
}
|
@ -1,18 +1,18 @@
|
||||
import React, { Component } from "react";
|
||||
import Navbar from "./navigation/Navbar";
|
||||
import { Grid, Row, Col } from "patternfly-react";
|
||||
import Header from "./navigation/Header";
|
||||
|
||||
export default class MainContainer extends Component {
|
||||
render() {
|
||||
const { children } = this.props;
|
||||
return (
|
||||
<div className="MainContent">
|
||||
<Navbar />
|
||||
<Grid fluid>
|
||||
<Row>
|
||||
<Col xs={12}>{children}</Col>
|
||||
</Row>
|
||||
</Grid>
|
||||
<div>
|
||||
<div className="pf-c-background-image" />
|
||||
<div className="pf-c-page" id="page-layout-horizontal-nav">
|
||||
<Header />
|
||||
<main role="main" className="pf-c-page__main">
|
||||
<section className="pf-c-page__main-section">{children}</section>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
72
src/layout/navigation/Header.js
Normal file
72
src/layout/navigation/Header.js
Normal file
@ -0,0 +1,72 @@
|
||||
import React, { Component } from "react";
|
||||
import styled from "styled-components";
|
||||
import { Link } from "react-router-dom";
|
||||
import { withRouter } from "react-router";
|
||||
import logo from "./logo.svg";
|
||||
|
||||
const AraImg = styled.img`
|
||||
height: 45px;
|
||||
`;
|
||||
|
||||
export class NavLink extends Component {
|
||||
render() {
|
||||
const { to, className, location, children, ...rest } = this.props;
|
||||
return (
|
||||
<Link
|
||||
to={to}
|
||||
className={`${className} ${
|
||||
location.pathname === to ? "pf-m-current" : ""
|
||||
}`}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class Header extends Component {
|
||||
render() {
|
||||
const { location } = this.props;
|
||||
return (
|
||||
<header role="banner" className="pf-c-page__header">
|
||||
<div className="pf-c-page__header-brand">
|
||||
<Link to="/playbooks" className="pf-c-page__header-brand-link">
|
||||
<AraImg className="pf-c-brand" src={logo} alt="Ara Logo" />
|
||||
</Link>
|
||||
</div>
|
||||
<div className="pf-c-page__header-nav">
|
||||
<nav
|
||||
className="pf-c-nav"
|
||||
id="page-layout-horizontal-nav-horizontal-nav"
|
||||
aria-label="Horizontal Nav Example"
|
||||
>
|
||||
<ul className="pf-c-nav__horizontal-list">
|
||||
<li className="pf-c-nav__item">
|
||||
<NavLink
|
||||
to="/playbooks"
|
||||
className="pf-c-nav__link"
|
||||
location={location}
|
||||
>
|
||||
Playbooks
|
||||
</NavLink>
|
||||
</li>
|
||||
<li className="pf-c-nav__item">
|
||||
<a
|
||||
href="https://ara.readthedocs.io/en/latest/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="pf-c-nav__link"
|
||||
>
|
||||
Documentation
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(Header);
|
@ -1,15 +0,0 @@
|
||||
import React, { Component } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export default class NavLink extends Component {
|
||||
render() {
|
||||
const { id, to, location, children, ...rest } = this.props;
|
||||
return (
|
||||
<li className={location.pathname === to ? "active" : ""}>
|
||||
<Link to={to} id={`navbar-navbar-primary__${id}-link`} {...rest}>
|
||||
{children}
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
import React, { Component } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { Link } from "react-router-dom";
|
||||
import { withRouter } from "react-router";
|
||||
|
||||
import logo from "./logo.svg";
|
||||
import NavLink from "./NavLink";
|
||||
|
||||
export class Navbar extends Component {
|
||||
render() {
|
||||
const { location, config } = this.props;
|
||||
const { ara_version, ansible_version, python_version } = config;
|
||||
return (
|
||||
<nav className="navbar navbar-default navbar-pf">
|
||||
<div className="navbar-header">
|
||||
<button
|
||||
type="button"
|
||||
className="navbar-toggle"
|
||||
data-toggle="collapse"
|
||||
data-target=".navbar-collapse-1"
|
||||
>
|
||||
<span className="sr-only">Toggle navigation</span>
|
||||
<span className="icon-bar" />
|
||||
<span className="icon-bar" />
|
||||
<span className="icon-bar" />
|
||||
</button>
|
||||
<Link
|
||||
to="/playbooks"
|
||||
id="navbar-navbar-header__playbooks-link"
|
||||
className="navbar-brand"
|
||||
>
|
||||
<img
|
||||
src={logo}
|
||||
alt="ARA: Ansible Run Analysis"
|
||||
width="81"
|
||||
height="32"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="collapse navbar-collapse navbar-collapse-1">
|
||||
<ul className="nav navbar-nav navbar-utility">
|
||||
<li>
|
||||
<a
|
||||
href="https://ara.readthedocs.io/en/latest/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Documentation
|
||||
</a>
|
||||
</li>
|
||||
{ara_version ? (
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/openstack/ara"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<strong>ARA</strong> {ara_version}
|
||||
</a>
|
||||
</li>
|
||||
) : null}
|
||||
{ansible_version ? (
|
||||
<li>
|
||||
<a
|
||||
href="https://www.ansible.com/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<strong>Ansible</strong> {ansible_version}
|
||||
</a>
|
||||
</li>
|
||||
) : null}
|
||||
{python_version ? (
|
||||
<li>
|
||||
<a
|
||||
href="https://www.python.org/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<strong>Python</strong> {python_version}
|
||||
</a>
|
||||
</li>
|
||||
) : null}
|
||||
</ul>
|
||||
<ul className="nav navbar-nav navbar-primary">
|
||||
<NavLink id="playbooks" to="/playbooks" location={location}>
|
||||
Playbooks reports
|
||||
</NavLink>
|
||||
<NavLink id="about" to="/about" location={location}>
|
||||
About
|
||||
</NavLink>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
config: state.config
|
||||
};
|
||||
}
|
||||
|
||||
export default withRouter(connect(mapStateToProps)(Navbar));
|
@ -1,9 +1,58 @@
|
||||
import React, { Component } from "react";
|
||||
import { Row, Col, ListView, Icon } from "patternfly-react";
|
||||
import { Link } from "react-router-dom";
|
||||
import PlaybookArgs from "./PlaybookArgs";
|
||||
import PlaybookHosts from "./PlaybookHosts";
|
||||
import PlaybookFiles from "./PlaybookFiles";
|
||||
import styled from "styled-components";
|
||||
|
||||
function _getIconInfo(status) {
|
||||
switch (status) {
|
||||
case "running":
|
||||
return {
|
||||
title: "Playbook is in progress.",
|
||||
icon: "fa-pause",
|
||||
color: "blue"
|
||||
};
|
||||
case "completed":
|
||||
return {
|
||||
title: "Playbook has completed successfully.",
|
||||
icon: "fa-check",
|
||||
color: "green"
|
||||
};
|
||||
case "failed":
|
||||
return {
|
||||
title: "Playbook has failed with one or more errors.",
|
||||
icon: "fa-warning",
|
||||
color: "red"
|
||||
};
|
||||
default:
|
||||
return {
|
||||
title: "Playbook's status is unknown.",
|
||||
icon: "fa-warning",
|
||||
color: "red"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const IconWrapper = styled.i`
|
||||
color: ${props => props.color};
|
||||
`;
|
||||
|
||||
class StatusIcon extends Component {
|
||||
render() {
|
||||
const { status } = this.props;
|
||||
const iconInfo = _getIconInfo(status);
|
||||
return (
|
||||
<IconWrapper
|
||||
color={iconInfo.color}
|
||||
className={`fas ${iconInfo.icon}`}
|
||||
title={iconInfo.title}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
const DataListCell = styled.div`
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
export default class Playbook extends Component {
|
||||
constructor(props) {
|
||||
@ -17,120 +66,74 @@ export default class Playbook extends Component {
|
||||
_toggleExpanded = selection => {
|
||||
this.setState(prevState => {
|
||||
if (selection === prevState.selection) {
|
||||
return { expanded: !prevState.expanded };
|
||||
return { expanded: !prevState.expanded, selection: null };
|
||||
} else {
|
||||
return { expanded: true, selection };
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
_getStatusMetadata(status) {
|
||||
switch(status) {
|
||||
case "running":
|
||||
return {
|
||||
"name": "running",
|
||||
"title": "Playbook is in progress.",
|
||||
"className": "pficon pficon-info list-view-pf-icon-md list-view-pf-icon-info"
|
||||
}
|
||||
case "completed":
|
||||
return {
|
||||
"name": "completed",
|
||||
"title": "Playbook has completed successfully.",
|
||||
"className": "pficon pficon-ok list-view-pf-icon-md list-view-pf-icon-success"
|
||||
}
|
||||
case "failed":
|
||||
return {
|
||||
"name": "failed",
|
||||
"title": "Playbook has failed with one or more errors.",
|
||||
"className": "pficon pficon-error-circle-o list-view-pf-icon-md list-view-pf-icon-danger"
|
||||
}
|
||||
case "unknown":
|
||||
return {
|
||||
"name": "unknown",
|
||||
"title": "Playbook's status is unknown.",
|
||||
"className": "pficon pficon-warning-triangle-o list-view-pf-icon-md list-view-pf-icon-warning"
|
||||
}
|
||||
default:
|
||||
return {
|
||||
"name": "unknown",
|
||||
"title": "Playbook's status is unknown.",
|
||||
"className": "pficon pficon-warning-triangle-o list-view-pf-icon-md list-view-pf-icon-warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { playbook } = this.props;
|
||||
const { expanded, selection } = this.state;
|
||||
const status_metadata = this._getStatusMetadata(playbook.status);
|
||||
const LeftIcon =
|
||||
<ListView.Icon
|
||||
name={status_metadata["name"]}
|
||||
size="md"
|
||||
title={status_metadata["title"]}
|
||||
className={status_metadata["className"]}
|
||||
/>
|
||||
|
||||
return (
|
||||
<ListView.Item
|
||||
checkboxInput={
|
||||
<Link to={`/playbooks/${playbook.id}`} className="navbar-brand">
|
||||
<Icon name="link" size="lg" />
|
||||
</Link>
|
||||
}
|
||||
leftContent={LeftIcon}
|
||||
additionalInfo={[
|
||||
<ListView.InfoItem key="args">
|
||||
<ListView.Expand
|
||||
expanded={expanded && selection === "args"}
|
||||
toggleExpanded={() => this._toggleExpanded("args")}
|
||||
>
|
||||
<Icon name="cogs" />
|
||||
Arguments
|
||||
</ListView.Expand>
|
||||
</ListView.InfoItem>,
|
||||
<ListView.InfoItem key="hosts">
|
||||
<ListView.Expand
|
||||
expanded={expanded && selection === "hosts"}
|
||||
toggleExpanded={() => this._toggleExpanded("hosts")}
|
||||
>
|
||||
<Icon name="server" />
|
||||
<b>{playbook.hosts.length}</b> Hosts
|
||||
</ListView.Expand>
|
||||
</ListView.InfoItem>,
|
||||
<ListView.InfoItem key="files">
|
||||
<ListView.Expand
|
||||
expanded={expanded && selection === "files"}
|
||||
toggleExpanded={() => this._toggleExpanded("files")}
|
||||
>
|
||||
<Icon name="folder-open" />
|
||||
<b>{playbook.files.length}</b> Files
|
||||
</ListView.Expand>
|
||||
</ListView.InfoItem>
|
||||
]}
|
||||
actions={
|
||||
<span>
|
||||
<Icon name="clock-o" size="lg" /> {Math.round(playbook.duration)} sec
|
||||
</span>
|
||||
}
|
||||
heading={
|
||||
playbook.name
|
||||
? playbook.name
|
||||
: playbook.file.path.split("/").slice(-1)[0]
|
||||
}
|
||||
stacked={false}
|
||||
compoundExpand
|
||||
compoundExpanded={expanded}
|
||||
onCloseCompoundExpand={() => this.setState({ expanded: false })}
|
||||
>
|
||||
<Row>
|
||||
<Col xs={12} sm={8} smOffset={2} md={6} mdOffset={3}>
|
||||
<ul className="pf-c-data-list pf-u-box-shadow-md">
|
||||
<li
|
||||
className={`pf-c-data-list__item ${expanded ? "pf-m-expanded" : ""}`}
|
||||
>
|
||||
<div className="pf-c-data-list__check">
|
||||
<StatusIcon status={playbook.status} />
|
||||
</div>
|
||||
<DataListCell className="pf-c-data-list__cell pf-m-flex-5">
|
||||
{playbook.file.path.split("/").slice(-1)[0]}
|
||||
</DataListCell>
|
||||
<DataListCell
|
||||
className="pf-c-data-list__cell pf-m-flex-1"
|
||||
onClick={() => this._toggleExpanded("args")}
|
||||
>
|
||||
<i
|
||||
className={`fas fa-angle-${
|
||||
selection === "args" ? "down" : "right"
|
||||
} pf-u-mr-xs`}
|
||||
/>
|
||||
<b>{Object.keys(playbook.arguments).length}</b> arguments
|
||||
</DataListCell>
|
||||
<DataListCell
|
||||
className="pf-c-data-list__cell pf-m-flex-1"
|
||||
onClick={() => this._toggleExpanded("hosts")}
|
||||
>
|
||||
<i
|
||||
className={`fas fa-angle-${
|
||||
selection === "hosts" ? "down" : "right"
|
||||
} pf-u-mr-xs`}
|
||||
/>
|
||||
<b>{playbook.hosts.length}</b> Hosts
|
||||
</DataListCell>
|
||||
<DataListCell
|
||||
className="pf-c-data-list__cell pf-m-flex-1"
|
||||
onClick={() => this._toggleExpanded("files")}
|
||||
>
|
||||
<i
|
||||
className={`fas fa-angle-${
|
||||
selection === "files" ? "down" : "right"
|
||||
} pf-u-mr-xs`}
|
||||
/>
|
||||
<b>{playbook.files.length}</b> Files
|
||||
</DataListCell>
|
||||
<DataListCell className="pf-c-data-list__cell pf-u-text-align-right">
|
||||
<i className={`fas fa-clock pf-u-mr-xs`} />
|
||||
{Math.round(playbook.duration)} sec
|
||||
</DataListCell>
|
||||
<section
|
||||
className="pf-c-data-list__expandable-content"
|
||||
aria-label="Primary Content Details"
|
||||
>
|
||||
{selection === "args" && <PlaybookArgs playbook={playbook} />}
|
||||
{selection === "hosts" && <PlaybookHosts playbook={playbook} />}
|
||||
{selection === "files" && <PlaybookFiles playbook={playbook} />}
|
||||
</Col>
|
||||
</Row>
|
||||
</ListView.Item>
|
||||
</section>
|
||||
</li>
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,35 +1,4 @@
|
||||
import React, { Component } from "react";
|
||||
import { OverlayTrigger, Tooltip, Icon } from "patternfly-react";
|
||||
|
||||
export class ParamatersHelpIcon extends Component {
|
||||
render() {
|
||||
return (
|
||||
<span style={{ float: "right" }}>
|
||||
<OverlayTrigger
|
||||
overlay={
|
||||
<Tooltip id="tooltip">
|
||||
<div>
|
||||
<h3>Tips: Arguments</h3>
|
||||
<hr />
|
||||
<p>
|
||||
This panel contains all the arguments and options passed to
|
||||
the ansible-playbook command.
|
||||
</p>
|
||||
<p>
|
||||
You can control which arguments ARA should ignore with the{" "}
|
||||
<code>ignored_arguments</code> configuration.
|
||||
</p>
|
||||
</div>
|
||||
</Tooltip>
|
||||
}
|
||||
placement="bottom"
|
||||
>
|
||||
<Icon name="question-circle" />
|
||||
</OverlayTrigger>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default class PlaybookArgs extends Component {
|
||||
constructor(props) {
|
||||
@ -55,35 +24,28 @@ export default class PlaybookArgs extends Component {
|
||||
arg => arg.toLowerCase().indexOf(search.toLowerCase()) !== -1
|
||||
);
|
||||
return (
|
||||
<div className="table-response">
|
||||
<div className="dataTables_header">
|
||||
<div className="dataTables_filter">
|
||||
<div>
|
||||
<div className="pf-l-grid pf-m-gutter pf-u-display-flex pf-u-align-items-center">
|
||||
<div className="pf-l-grid__item">
|
||||
<input
|
||||
className="form-control"
|
||||
className="pf-c-form-control"
|
||||
placeholder="Search an argument"
|
||||
type="search"
|
||||
value={search}
|
||||
onChange={e => this.setState({ search: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
<div className="dataTables_info">
|
||||
Showing <b>{filteredArgs.length}</b> of{" "}
|
||||
<b>{args.length}</b> args
|
||||
<ParamatersHelpIcon />
|
||||
<div className="pf-l-grid__item">
|
||||
{`Showing ${filteredArgs.length} of ${args.length} args`}
|
||||
</div>
|
||||
</div>
|
||||
<table className="table table-striped table-bordered table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Argument</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<table className="pf-c-table pf-m-compact pf-m-grid-md" role="grid">
|
||||
<tbody>
|
||||
{filteredArgs.map((arg, i) => (
|
||||
<tr key={i}>
|
||||
<td>{arg}</td>
|
||||
<td>{this._renderArg(playbook.arguments[arg])}</td>
|
||||
<td className="pf-m-width-30">{arg}</td>
|
||||
<td className="pf-m-width-70">{this._renderArg(playbook.arguments[arg])}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
|
@ -1,5 +1,9 @@
|
||||
import React, { Component } from "react";
|
||||
import { Button, Icon, Modal } from "patternfly-react";
|
||||
import styled from "styled-components";
|
||||
|
||||
const ModalBox = styled.div`
|
||||
position: absolute;
|
||||
`;
|
||||
|
||||
export default class PlaybookFiles extends Component {
|
||||
state = {
|
||||
@ -16,48 +20,52 @@ export default class PlaybookFiles extends Component {
|
||||
const { playbook } = this.props;
|
||||
const { showModal, filePath, fileContent } = this.state;
|
||||
return (
|
||||
<div className="table-response">
|
||||
<Modal show={showModal} onHide={this.close} bsSize="large">
|
||||
<Modal.Header>
|
||||
<button
|
||||
className="close"
|
||||
onClick={this.close}
|
||||
aria-hidden="true"
|
||||
aria-label="Close"
|
||||
<div>
|
||||
{showModal && (
|
||||
<ModalBox>
|
||||
<div
|
||||
className="pf-c-modal-box pf-m-lg"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="modal-title"
|
||||
aria-describedby="modal-description"
|
||||
>
|
||||
<Icon type="pf" name="close" />
|
||||
</button>
|
||||
<Modal.Title>{filePath}</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<pre>
|
||||
<code>{fileContent}</code>
|
||||
</pre>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button
|
||||
bsStyle="default"
|
||||
className="btn-cancel"
|
||||
onClick={this.close}
|
||||
>
|
||||
close
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
<table className="table table-striped table-bordered table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<div className="pf-c-modal-box__close">
|
||||
<button
|
||||
className="pf-c-button pf-m-plain"
|
||||
aria-label="Close"
|
||||
onClick={this.close}
|
||||
>
|
||||
<i className="fas fa-times" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
<header className="pf-c-modal-box__header">
|
||||
<h1 className="pf-c-modal-box__header-title" id="modal-title">
|
||||
{filePath}
|
||||
</h1>
|
||||
</header>
|
||||
<div className="pf-c-modal-box__body" id="modal-description">
|
||||
<pre>
|
||||
<code>{fileContent}</code>
|
||||
</pre>
|
||||
</div>
|
||||
<footer className="pf-c-modal-box__footer">
|
||||
<button type="button" onClick={this.close}>
|
||||
close
|
||||
</button>
|
||||
</footer>
|
||||
</div>
|
||||
</ModalBox>
|
||||
)}
|
||||
<table className="pf-c-table pf-m-compact pf-m-grid-md" role="grid">
|
||||
<tbody>
|
||||
{playbook.files.map(file => (
|
||||
<tr key={file.id}>
|
||||
<td>{file.path}</td>
|
||||
<td className="text-center">
|
||||
<Button
|
||||
bsStyle="primary"
|
||||
<td>
|
||||
<button
|
||||
type="button"
|
||||
className="pf-c-button pf-m-secondary"
|
||||
onClick={() =>
|
||||
this.setState({
|
||||
showModal: true,
|
||||
@ -66,8 +74,8 @@ export default class PlaybookFiles extends Component {
|
||||
})
|
||||
}
|
||||
>
|
||||
<Icon name="eye" />
|
||||
</Button>
|
||||
See content
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
|
@ -1,30 +1,4 @@
|
||||
import React, { Component } from "react";
|
||||
import { OverlayTrigger, Tooltip, Icon } from "patternfly-react";
|
||||
|
||||
export class HostsHelpIcon extends Component {
|
||||
render() {
|
||||
return (
|
||||
<span style={{ float: "right" }}>
|
||||
<OverlayTrigger
|
||||
overlay={
|
||||
<Tooltip id="tooltip">
|
||||
<div>
|
||||
<h3>Tips: Hosts</h3>
|
||||
<hr />
|
||||
<p>
|
||||
This panel contains all the hosts involved in the playbook.
|
||||
</p>
|
||||
</div>
|
||||
</Tooltip>
|
||||
}
|
||||
placement="bottom"
|
||||
>
|
||||
<Icon name="question-circle" />
|
||||
</OverlayTrigger>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default class PlaybookHosts extends Component {
|
||||
constructor(props) {
|
||||
@ -41,35 +15,30 @@ export default class PlaybookHosts extends Component {
|
||||
host => host.name.toLowerCase().indexOf(search.toLowerCase()) !== -1
|
||||
);
|
||||
return (
|
||||
<div className="table-response">
|
||||
<div className="dataTables_header">
|
||||
<div className="dataTables_filter">
|
||||
<div>
|
||||
<div className="pf-l-grid pf-m-gutter pf-u-display-flex pf-u-align-items-center">
|
||||
<div className="pf-l-grid__item">
|
||||
<input
|
||||
className="form-control"
|
||||
className="pf-c-form-control"
|
||||
placeholder="Search a host"
|
||||
type="search"
|
||||
value={search}
|
||||
onChange={e => this.setState({ search: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
<div className="dataTables_info">
|
||||
Showing <b>{filteredHosts.length}</b> of{" "}
|
||||
<b>{playbook.hosts.length}</b> hosts
|
||||
<HostsHelpIcon />
|
||||
<div className="pf-l-grid__item">
|
||||
{`Showing ${filteredHosts.length} of ${
|
||||
playbook.hosts.length
|
||||
} hosts`}
|
||||
</div>
|
||||
</div>
|
||||
<table className="table table-striped table-bordered table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Alias</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<table className="pf-c-table pf-m-compact pf-m-grid-md" role="grid">
|
||||
<tbody>
|
||||
{filteredHosts.map(host => (
|
||||
<tr key={host.id}>
|
||||
<td>{host.name}</td>
|
||||
<td>{host.alias}</td>
|
||||
<td className="pf-m-width-30">{host.name}</td>
|
||||
<td className="pf-m-width-70">{host.alias}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
|
@ -1,24 +1,67 @@
|
||||
import React, { Component } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { ListView } from "patternfly-react";
|
||||
import { isEmpty } from "lodash";
|
||||
|
||||
import { MainContainer } from "../containers";
|
||||
import { getPlaybooks } from "./playbooksActions";
|
||||
import Playbook from "./Playbook";
|
||||
|
||||
export class PlaybooksContainer extends Component {
|
||||
state = {
|
||||
isLoading: true
|
||||
};
|
||||
componentDidMount() {
|
||||
this.props.getPlaybooks();
|
||||
this.props
|
||||
.getPlaybooks()
|
||||
.catch(error => console.log(error))
|
||||
.then(() => this.setState({ isLoading: false }));
|
||||
}
|
||||
render() {
|
||||
const { playbooks } = this.props;
|
||||
const { isLoading } = this.state;
|
||||
return (
|
||||
<MainContainer>
|
||||
<ListView>
|
||||
{playbooks.map(playbook => (
|
||||
<Playbook key={playbook.id} playbook={playbook} />
|
||||
))}
|
||||
</ListView>
|
||||
{isLoading && (
|
||||
<div className="pf-l-bullseye">
|
||||
<div className="pf-l-bullseye__item">
|
||||
<div className="pf-c-card">
|
||||
<div className="pf-c-card__body">loading</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!isLoading && isEmpty(playbooks) && (
|
||||
<div className="pf-l-bullseye">
|
||||
<div className="pf-l-bullseye__item">
|
||||
<div className="pf-c-card">
|
||||
<div className="pf-c-card__body">
|
||||
<div className="pf-c-empty-state">
|
||||
<i
|
||||
className="fas fa-cubes pf-c-empty-state__icon"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<h1 className="pf-c-title pf-m-lg">No playbooks</h1>
|
||||
<p className="pf-c-empty-state__body">
|
||||
There is no playbook available on this instance of Ara
|
||||
</p>
|
||||
<div className="pf-c-empty-state__action">
|
||||
<a
|
||||
href="https://ara.readthedocs.io/en/latest/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
See documentation
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{playbooks.map(playbook => (
|
||||
<Playbook key={playbook.id} playbook={playbook} />
|
||||
))}
|
||||
</MainContainer>
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user