Move Organization screens and tests into new folder structure

This commit is contained in:
Marliana Lara
2018-12-21 16:15:39 -05:00
parent f521fe5cbc
commit d040f063e9
14 changed files with 120 additions and 124 deletions

View File

@@ -0,0 +1,128 @@
import React, { Component, Fragment } from 'react';
import { i18nMark } from '@lingui/react';
import {
Switch,
Route,
withRouter,
} from 'react-router-dom';
import {
PageSection
} from '@patternfly/react-core';
import OrganizationBreadcrumb from '../../components/OrganizationBreadcrumb';
import OrganizationDetail from './OrganizationDetail';
import OrganizationEdit from './OrganizationEdit';
class Organization extends Component {
constructor (props) {
super(props);
let { breadcrumb: parentBreadcrumbObj, organization } = props.location.state || {};
if (!parentBreadcrumbObj) {
parentBreadcrumbObj = 'loading';
}
if (!organization) {
organization = 'loading';
}
this.state = {
parentBreadcrumbObj,
organization,
error: false,
loading: false,
mounted: false
};
this.fetchOrganization = this.fetchOrganization.bind(this);
}
componentDidMount () {
this.setState({ mounted: true }, () => {
const { organization } = this.state;
if (organization === 'loading') {
this.fetchOrganization();
}
});
}
componentWillUnmount () {
this.setState({ mounted: false });
}
async fetchOrganization () {
const { mounted } = this.state;
const { api } = this.props;
if (mounted) {
this.setState({ error: false, loading: true });
const { match } = this.props;
const { parentBreadcrumbObj, organization } = this.state;
try {
const { data } = await api.getOrganizationDetails(match.params.id);
if (organization === 'loading') {
this.setState({ organization: data });
}
const { name } = data;
if (parentBreadcrumbObj === 'loading') {
this.setState({ parentBreadcrumbObj: [{ name: i18nMark('Organizations'), url: '/organizations' }, { name, url: match.url }] });
}
} catch (err) {
this.setState({ error: true });
} finally {
this.setState({ loading: false });
}
}
}
render () {
const { location, match } = this.props;
const { parentBreadcrumbObj, organization, error, loading } = this.state;
const params = new URLSearchParams(location.search);
const currentTab = params.get('tab') || 'details';
return (
<Fragment>
<OrganizationBreadcrumb
parentObj={parentBreadcrumbObj}
currentTab={currentTab}
location={location}
organization={organization}
/>
<PageSection>
<Switch>
<Route
path={`${match.path}/edit`}
component={() => (
<OrganizationEdit
location={location}
match={match}
parentBreadcrumbObj={parentBreadcrumbObj}
organization={organization}
params={params}
currentTab={currentTab}
/>
)}
/>
<Route
path={`${match.path}`}
component={() => (
<OrganizationDetail
location={location}
match={match}
parentBreadcrumbObj={parentBreadcrumbObj}
organization={organization}
params={params}
currentTab={currentTab}
/>
)}
/>
</Switch>
{error ? 'error!' : ''}
{loading ? 'loading...' : ''}
</PageSection>
</Fragment>
);
}
}
export default withRouter(Organization);

View File

@@ -0,0 +1,105 @@
import React, { Fragment } from 'react';
import { I18n } from '@lingui/react';
import { Trans, t } from '@lingui/macro';
import {
Card,
CardHeader,
CardBody,
} from '@patternfly/react-core';
import {
Switch,
Link,
Route
} from 'react-router-dom';
import Tab from '../../../../components/Tabs/Tab';
import Tabs from '../../../../components/Tabs/Tabs';
import getTabName from '../../utils';
const OrganizationDetail = ({
location,
match,
parentBreadcrumbObj,
organization,
params,
currentTab
}) => {
// TODO: set objectName by param or through grabbing org detail get from api
const tabList=['details', 'access', 'teams', 'notifications'];
const deleteResourceView = () => (
<Fragment>
<Trans>{`deleting ${currentTab} association with orgs `}</Trans>
<Link to={{ pathname: `${match.url}`, search: `?${params.toString()}`, state: { breadcrumb: parentBreadcrumbObj, organization } }}>
<Trans>{`confirm removal of ${currentTab}/cancel and go back to ${currentTab} view.`}</Trans>
</Link>
</Fragment>
);
const addResourceView = () => (
<Fragment>
<Trans>{`adding ${currentTab} `}</Trans>
<Link to={{ pathname: `${match.url}`, search: `?${params.toString()}`, state: { breadcrumb: parentBreadcrumbObj, organization } }}>
<Trans>{`save/cancel and go back to ${currentTab} view`}</Trans>
</Link>
</Fragment>
);
const resourceView = () => (
<Fragment>
<Trans>{`${currentTab} detail view `}</Trans>
<Link to={{ pathname: `${match.url}/add-resource`, search: `?${params.toString()}`, state: { breadcrumb: parentBreadcrumbObj, organization } }}>
<Trans>{`add ${currentTab}`}</Trans>
</Link>
{' '}
<Link to={{ pathname: `${match.url}/delete-resources`, search: `?${params.toString()}`, state: { breadcrumb: parentBreadcrumbObj, organization } }}>
<Trans>{`delete ${currentTab}`}</Trans>
</Link>
</Fragment>
);
return (
<Card className="at-c-orgPane">
<CardHeader>
<I18n>
{({ i18n }) => (
<Tabs labelText={i18n._(t`Organization detail tabs`)}>
{tabList.map(tab => (
<Tab
key={tab}
tab={tab}
location={location}
match={match}
currentTab={currentTab}
breadcrumb={parentBreadcrumbObj}
>
<Trans>{getTabName(tab)}</Trans>
</Tab>
))}
</Tabs>
)}
</I18n>
</CardHeader>
<CardBody>
{(currentTab && currentTab !== 'details') ? (
<Switch>
<Route path={`${match.path}/delete-resources`} component={() => deleteResourceView()} />
<Route path={`${match.path}/add-resource`} component={() => addResourceView()} />
<Route path={`${match.path}`} component={() => resourceView()} />
</Switch>
) : (
<Fragment>
{'detail view '}
<Link to={{ pathname: `${match.url}/edit`, state: { breadcrumb: parentBreadcrumbObj, organization } }}>
{'edit'}
</Link>
</Fragment>
)}
</CardBody>
</Card>
);
};
export default OrganizationDetail;

View File

@@ -0,0 +1,25 @@
import React from 'react';
import { Trans } from '@lingui/macro';
import {
Card,
CardBody
} from '@patternfly/react-core';
import {
Link
} from 'react-router-dom';
const OrganizationEdit = ({ match, parentBreadcrumbObj, organization }) => {
return (
<Card className="at-c-orgPane">
<CardBody>
<Trans>edit view </Trans>
<Link to={{ pathname: `/organizations/${match.params.id}`, state: { breadcrumb: parentBreadcrumbObj, organization } }}>
<Trans>save/cancel and go back to view</Trans>
</Link>
</CardBody>
</Card>
);
};
export default OrganizationEdit;

View File

@@ -0,0 +1,152 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { withRouter } from 'react-router-dom';
import { Trans } from '@lingui/macro';
import {
PageSection,
PageSectionVariants,
Title,
Form,
FormGroup,
TextInput,
ActionGroup,
Toolbar,
ToolbarGroup,
Button,
Gallery,
Card,
CardBody,
} from '@patternfly/react-core';
import { ConfigContext } from '../../../context';
import AnsibleSelect from '../../../components/AnsibleSelect'
const { light } = PageSectionVariants;
class OrganizationAdd extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.onSelectChange = this.onSelectChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.resetForm = this.resetForm.bind(this);
this.onCancel = this.onCancel.bind(this);
}
state = {
name: '',
description: '',
instanceGroups: '',
custom_virtualenv: '',
error:'',
};
onSelectChange(value, _) {
this.setState({ custom_virtualenv: value });
};
resetForm() {
this.setState({
...this.state,
name: '',
description: ''
})
}
handleChange(_, evt) {
this.setState({ [evt.target.name]: evt.target.value });
}
async onSubmit() {
const { api } = this.props;
const data = Object.assign({}, { ...this.state });
await api.createOrganization(data);
this.resetForm();
}
onCancel() {
this.props.history.push('/organizations');
}
render() {
const { name } = this.state;
const enabled = name.length > 0; // TODO: add better form validation
return (
<Fragment>
<PageSection variant={light} className="pf-m-condensed">
<Title size="2xl">
<Trans>Organization Add</Trans>
</Title>
</PageSection>
<PageSection>
<Card>
<CardBody>
<Form autoComplete="off">
<Gallery gutter="md">
<FormGroup
label="Name"
isRequired
fieldId="add-org-form-name"
>
<TextInput
isRequired
type="text"
id="add-org-form-name"
name="name"
value={this.state.name}
onChange={this.handleChange}
/>
</FormGroup>
<FormGroup label="Description" fieldId="add-org-form-description">
<TextInput
id="add-org-form-description"
name="description"
value={this.state.description}
onChange={this.handleChange}
/>
</FormGroup>
{/* LOOKUP MODAL PLACEHOLDER */}
<FormGroup label="Instance Groups" fieldId="simple-form-instance-groups">
<TextInput
id="add-org-form-instance-groups"
name="instance-groups"
value={this.state.instanceGroups}
onChange={this.handleChange}
/>
</FormGroup>
<ConfigContext.Consumer>
{({ custom_virtualenvs }) =>
<AnsibleSelect
labelName="Ansible Environment"
selected={this.state.custom_virtualenv}
selectChange={this.onSelectChange}
data={custom_virtualenvs}
/>
}
</ConfigContext.Consumer>
</Gallery>
<ActionGroup className="at-align-right">
<Toolbar>
<ToolbarGroup>
<Button className="at-C-SubmitButton" variant="primary" onClick={this.onSubmit} isDisabled={!enabled}>Save</Button>
</ToolbarGroup>
<ToolbarGroup>
<Button className="at-C-CancelButton" variant="secondary" onClick={this.onCancel}>Cancel</Button>
</ToolbarGroup>
</Toolbar>
</ActionGroup>
</Form>
</CardBody>
</Card>
</PageSection>
</Fragment>
);
}
}
OrganizationAdd.contextTypes = {
custom_virtualenvs: PropTypes.array,
};
export default withRouter(OrganizationAdd);

View File

@@ -0,0 +1,251 @@
import React, {
Component,
Fragment
} from 'react';
import {
withRouter
} from 'react-router-dom';
import { I18n, i18nMark } from '@lingui/react';
import { Trans, t } from '@lingui/macro';
import {
PageSection,
PageSectionVariants,
Title,
} from '@patternfly/react-core';
import DataListToolbar from '../../../components/DataListToolbar';
import OrganizationListItem from '../components/OrganizationListItem';
import Pagination from '../../../components/Pagination';
import {
encodeQueryString,
parseQueryString,
} from '../../../qs';
class OrganizationsList extends Component {
columns = [
{ name: i18nMark('Name'), key: 'name', isSortable: true },
{ name: i18nMark('Modified'), key: 'modified', isSortable: true, isNumeric: true },
{ name: i18nMark('Created'), key: 'created', isSortable: true, isNumeric: true },
];
defaultParams = {
page: 1,
page_size: 5,
order_by: 'name',
};
pageSizeOptions = [5, 10, 25, 50];
constructor (props) {
super(props);
const { page, page_size } = this.getQueryParams();
this.state = {
page,
page_size,
sortedColumnKey: 'name',
sortOrder: 'ascending',
count: null,
error: null,
loading: true,
results: [],
selected: [],
};
this.onSearch = this.onSearch.bind(this);
this.getQueryParams = this.getQueryParams.bind(this);
this.onSort = this.onSort.bind(this);
this.onSetPage = this.onSetPage.bind(this);
this.onSelectAll = this.onSelectAll.bind(this);
this.onSelect = this.onSelect.bind(this);
this.updateUrl = this.updateUrl.bind(this);
this.fetchOrganizations = this.fetchOrganizations.bind(this);
}
componentDidMount () {
const queryParams = this.getQueryParams();
this.fetchOrganizations(queryParams);
}
onSearch () {
const { sortedColumnKey, sortOrder } = this.state;
this.onSort(sortedColumnKey, sortOrder);
}
getQueryParams (overrides = {}) {
const { location } = this.props;
const { search } = location;
const searchParams = parseQueryString(search.substring(1));
return Object.assign({}, this.defaultParams, searchParams, overrides);
}
onSort(sortedColumnKey, sortOrder) {
const { page_size } = this.state;
let order_by = sortedColumnKey;
if (sortOrder === 'descending') {
order_by = `-${order_by}`;
}
const queryParams = this.getQueryParams({ order_by, page_size });
this.fetchOrganizations(queryParams);
}
onSetPage (pageNumber, pageSize) {
const page = parseInt(pageNumber, 10);
const page_size = parseInt(pageSize, 10);
const queryParams = this.getQueryParams({ page, page_size });
this.fetchOrganizations(queryParams);
}
onSelectAll (isSelected) {
const { results } = this.state;
const selected = isSelected ? results.map(o => o.id) : [];
this.setState({ selected });
}
onSelect (id) {
const { selected } = this.state;
const isSelected = selected.includes(id);
if (isSelected) {
this.setState({ selected: selected.filter(s => s !== id) });
} else {
this.setState({ selected: selected.concat(id) });
}
}
updateUrl (queryParams) {
const { history, location } = this.props;
const pathname = '/organizations';
const search = `?${encodeQueryString(queryParams)}`;
if (search !== location.search) {
history.replace({ pathname, search });
}
}
async fetchOrganizations (queryParams) {
const { api } = this.props;
const { page, page_size, order_by } = queryParams;
let sortOrder = 'ascending';
let sortedColumnKey = order_by;
if (order_by.startsWith('-')) {
sortOrder = 'descending';
sortedColumnKey = order_by.substring(1);
}
this.setState({ error: false, loading: true });
try {
const { data } = await api.getOrganizations(queryParams);
const { count, results } = data;
const pageCount = Math.ceil(count / page_size);
this.setState({
count,
page,
pageCount,
page_size,
sortOrder,
sortedColumnKey,
results,
selected: [],
});
this.updateUrl(queryParams);
} catch (err) {
this.setState({ error: true });
} finally {
this.setState({ loading: false });
}
}
render () {
const {
light,
medium,
} = PageSectionVariants;
const {
count,
error,
loading,
page,
pageCount,
page_size,
sortedColumnKey,
sortOrder,
results,
selected,
} = this.state;
const { match } = this.props;
const parentBreadcrumb = { name: i18nMark('Organizations'), url: match.url };
return (
<Fragment>
<PageSection variant={light} className="pf-m-condensed">
<Title size="2xl">
<Trans>Organizations</Trans>
</Title>
</PageSection>
<PageSection variant={medium}>
<DataListToolbar
addUrl={`${match.url}/add`}
isAllSelected={selected.length === results.length}
sortedColumnKey={sortedColumnKey}
sortOrder={sortOrder}
columns={this.columns}
onSearch={this.onSearch}
onSort={this.onSort}
onSelectAll={this.onSelectAll}
/>
<I18n>
{({ i18n }) => (
<ul className="pf-c-data-list" aria-label={i18n._(t`Organizations List`)}>
{ results.map(o => (
<OrganizationListItem
key={o.id}
itemId={o.id}
name={o.name}
detailUrl={`${match.url}/${o.id}`}
parentBreadcrumb={parentBreadcrumb}
userCount={o.summary_fields.related_field_counts.users}
teamCount={o.summary_fields.related_field_counts.teams}
isSelected={selected.includes(o.id)}
onSelect={() => this.onSelect(o.id)}
/>
))}
</ul>
)}
</I18n>
<Pagination
count={count}
page={page}
pageCount={pageCount}
page_size={page_size}
pageSizeOptions={this.pageSizeOptions}
onSetPage={this.onSetPage}
/>
{ loading ? <div>loading...</div> : '' }
{ error ? <div>error</div> : '' }
</PageSection>
</Fragment>
);
}
}
export default withRouter(OrganizationsList);