mirror of
https://github.com/ansible/awx.git
synced 2026-05-23 16:47:45 -02:30
add patternfly tables; add PaginatedTable
This commit is contained in:
52
awx/ui_next/package-lock.json
generated
52
awx/ui_next/package-lock.json
generated
@@ -3194,6 +3194,58 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-4.7.22.tgz",
|
"resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-4.7.22.tgz",
|
||||||
"integrity": "sha512-ojNuSNJx6CkNtsSFseZ2SJEVyzPMFYh0jOs204ICzYM1+fn9acsIi3Co0bcskFRzw8F6e2/x+8uVNx6QI8elxg=="
|
"integrity": "sha512-ojNuSNJx6CkNtsSFseZ2SJEVyzPMFYh0jOs204ICzYM1+fn9acsIi3Co0bcskFRzw8F6e2/x+8uVNx6QI8elxg=="
|
||||||
},
|
},
|
||||||
|
"@patternfly/react-table": {
|
||||||
|
"version": "4.19.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@patternfly/react-table/-/react-table-4.19.15.tgz",
|
||||||
|
"integrity": "sha512-WeeiOlJs96QfT7/TChpaCUAsl0hsWsLelJIR68b0LA+gzTEJzaXn0m5NL9ht4LL3edT4hwMz0jVz98xkStmvBg==",
|
||||||
|
"requires": {
|
||||||
|
"@patternfly/patternfly": "4.59.1",
|
||||||
|
"@patternfly/react-core": "^4.75.10",
|
||||||
|
"@patternfly/react-icons": "^4.7.16",
|
||||||
|
"@patternfly/react-styles": "^4.7.13",
|
||||||
|
"@patternfly/react-tokens": "^4.9.16",
|
||||||
|
"lodash": "^4.17.19",
|
||||||
|
"tslib": "1.13.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@patternfly/react-core": {
|
||||||
|
"version": "4.79.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-4.79.2.tgz",
|
||||||
|
"integrity": "sha512-TCWi5Hu8+gpqFVAL4ZMXCRLbRfayM7wJ8+/Ob4rfhC61qm36CZNAcqWOmuV8bghOzB29INUMNShggtuiUa5mkg==",
|
||||||
|
"requires": {
|
||||||
|
"@patternfly/react-icons": "^4.7.18",
|
||||||
|
"@patternfly/react-styles": "^4.7.16",
|
||||||
|
"@patternfly/react-tokens": "^4.9.18",
|
||||||
|
"focus-trap": "4.0.2",
|
||||||
|
"react-dropzone": "9.0.0",
|
||||||
|
"tippy.js": "5.1.2",
|
||||||
|
"tslib": "1.13.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@patternfly/react-icons": {
|
||||||
|
"version": "4.7.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-4.7.18.tgz",
|
||||||
|
"integrity": "sha512-Kd0JjeVCESpMJGb5ZkLXvAdCuklV9dYGUkcTO18WMyXQ57s9+xXjVA77wojmp6Ru1ZCWOP5bLXZOKmwVnOfUpQ=="
|
||||||
|
},
|
||||||
|
"@patternfly/react-tokens": {
|
||||||
|
"version": "4.9.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-4.9.18.tgz",
|
||||||
|
"integrity": "sha512-zQfqwKtoz1hDngyiGnF6oHeESDtgNY6C79Db97JxMMuRBV7i+5f6uC/DrYhcqNtqHA7mxrVJg0SM1xnPSAW9lA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@patternfly/react-styles": {
|
||||||
|
"version": "4.7.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-4.7.16.tgz",
|
||||||
|
"integrity": "sha512-bJmRrYKXgHGPPwLHg/gy1tDb/qEV6JpFLgkelLuz38czXeBnPpAUn9yKry3wNr95VQGERT6FcLsWjXKPY1x42Q=="
|
||||||
|
},
|
||||||
|
"tslib": {
|
||||||
|
"version": "1.13.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
|
||||||
|
"integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@patternfly/react-tokens": {
|
"@patternfly/react-tokens": {
|
||||||
"version": "4.9.22",
|
"version": "4.9.22",
|
||||||
"resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-4.9.22.tgz",
|
"resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-4.9.22.tgz",
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
"@patternfly/patternfly": "4.70.2",
|
"@patternfly/patternfly": "4.70.2",
|
||||||
"@patternfly/react-core": "4.84.3",
|
"@patternfly/react-core": "4.84.3",
|
||||||
"@patternfly/react-icons": "4.7.22",
|
"@patternfly/react-icons": "4.7.22",
|
||||||
|
"@patternfly/react-table": "^4.19.15",
|
||||||
"ansi-to-html": "^0.6.11",
|
"ansi-to-html": "^0.6.11",
|
||||||
"axios": "^0.18.1",
|
"axios": "^0.18.1",
|
||||||
"codemirror": "^5.47.0",
|
"codemirror": "^5.47.0",
|
||||||
|
|||||||
33
awx/ui_next/src/components/PaginatedTable/ActionsTd.jsx
Normal file
33
awx/ui_next/src/components/PaginatedTable/ActionsTd.jsx
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import 'styled-components/macro';
|
||||||
|
import React from 'react';
|
||||||
|
import { Td } from '@patternfly/react-table';
|
||||||
|
import styled, { css } from 'styled-components';
|
||||||
|
|
||||||
|
// table cells will automatically grow beyond specified width to accomodate
|
||||||
|
// multiple action buttons
|
||||||
|
const ActionsGrid = styled.div`
|
||||||
|
display: grid;
|
||||||
|
grid-gap: 16px;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
${props => {
|
||||||
|
const columns = '40px '.repeat(props.numActions || 1);
|
||||||
|
return css`
|
||||||
|
grid-template-columns: ${columns};
|
||||||
|
`;
|
||||||
|
}}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default function ActionsTd({ numActions = 1, children }) {
|
||||||
|
const width = numActions * 40;
|
||||||
|
return (
|
||||||
|
<Td
|
||||||
|
css={`
|
||||||
|
text-align: right;
|
||||||
|
--pf-c-table--cell--Width: ${width}px;
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<ActionsGrid numActions={numActions}>{children}</ActionsGrid>
|
||||||
|
</Td>
|
||||||
|
);
|
||||||
|
}
|
||||||
227
awx/ui_next/src/components/PaginatedTable/PaginatedTable.jsx
Normal file
227
awx/ui_next/src/components/PaginatedTable/PaginatedTable.jsx
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
import React, { Fragment } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { TableComposable, Tbody } from '@patternfly/react-table';
|
||||||
|
import { withI18n } from '@lingui/react';
|
||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import { withRouter } from 'react-router-dom';
|
||||||
|
|
||||||
|
import ListHeader from '../ListHeader';
|
||||||
|
import ContentEmpty from '../ContentEmpty';
|
||||||
|
import ContentError from '../ContentError';
|
||||||
|
import ContentLoading from '../ContentLoading';
|
||||||
|
import Pagination from '../Pagination';
|
||||||
|
import DataListToolbar from '../DataListToolbar';
|
||||||
|
|
||||||
|
import {
|
||||||
|
encodeNonDefaultQueryString,
|
||||||
|
parseQueryString,
|
||||||
|
replaceParams,
|
||||||
|
} from '../../util/qs';
|
||||||
|
|
||||||
|
import { QSConfig, SearchColumns, SortColumns } from '../../types';
|
||||||
|
|
||||||
|
import PaginatedTableRow from './PaginatedTableRow';
|
||||||
|
|
||||||
|
class PaginatedTable extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.handleSetPage = this.handleSetPage.bind(this);
|
||||||
|
this.handleSetPageSize = this.handleSetPageSize.bind(this);
|
||||||
|
this.handleListItemSelect = this.handleListItemSelect.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleListItemSelect = (id = 0) => {
|
||||||
|
const { items, onRowClick } = this.props;
|
||||||
|
const match = items.find(item => item.id === Number(id));
|
||||||
|
onRowClick(match);
|
||||||
|
};
|
||||||
|
|
||||||
|
handleSetPage(event, pageNumber) {
|
||||||
|
const { history, qsConfig } = this.props;
|
||||||
|
const { search } = history.location;
|
||||||
|
const oldParams = parseQueryString(qsConfig, search);
|
||||||
|
this.pushHistoryState(replaceParams(oldParams, { page: pageNumber }));
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSetPageSize(event, pageSize, page) {
|
||||||
|
const { history, qsConfig } = this.props;
|
||||||
|
const { search } = history.location;
|
||||||
|
const oldParams = parseQueryString(qsConfig, search);
|
||||||
|
this.pushHistoryState(
|
||||||
|
replaceParams(oldParams, { page_size: pageSize, page })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pushHistoryState(params) {
|
||||||
|
const { history, qsConfig } = this.props;
|
||||||
|
const { pathname } = history.location;
|
||||||
|
const encodedParams = encodeNonDefaultQueryString(qsConfig, params);
|
||||||
|
history.push(encodedParams ? `${pathname}?${encodedParams}` : pathname);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
contentError,
|
||||||
|
hasContentLoading,
|
||||||
|
emptyStateControls,
|
||||||
|
items,
|
||||||
|
itemCount,
|
||||||
|
qsConfig,
|
||||||
|
headerRow,
|
||||||
|
renderRow,
|
||||||
|
toolbarSearchColumns,
|
||||||
|
toolbarSearchableKeys,
|
||||||
|
toolbarRelatedSearchableKeys,
|
||||||
|
toolbarSortColumns,
|
||||||
|
pluralizedItemName,
|
||||||
|
showPageSizeOptions,
|
||||||
|
location,
|
||||||
|
i18n,
|
||||||
|
renderToolbar,
|
||||||
|
} = this.props;
|
||||||
|
const searchColumns = toolbarSearchColumns.length
|
||||||
|
? toolbarSearchColumns
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
name: i18n._(t`Name`),
|
||||||
|
key: 'name',
|
||||||
|
isDefault: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const sortColumns = toolbarSortColumns.length
|
||||||
|
? toolbarSortColumns
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
name: i18n._(t`Name`),
|
||||||
|
key: 'name',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const queryParams = parseQueryString(qsConfig, location.search);
|
||||||
|
|
||||||
|
const dataListLabel = i18n._(t`${pluralizedItemName} List`);
|
||||||
|
const emptyContentMessage = i18n._(
|
||||||
|
t`Please add ${pluralizedItemName} to populate this list `
|
||||||
|
);
|
||||||
|
const emptyContentTitle = i18n._(t`No ${pluralizedItemName} Found `);
|
||||||
|
|
||||||
|
let Content;
|
||||||
|
if (hasContentLoading && items.length <= 0) {
|
||||||
|
Content = <ContentLoading />;
|
||||||
|
} else if (contentError) {
|
||||||
|
Content = <ContentError error={contentError} />;
|
||||||
|
} else if (items.length <= 0) {
|
||||||
|
Content = (
|
||||||
|
<ContentEmpty title={emptyContentTitle} message={emptyContentMessage} />
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Content = (
|
||||||
|
<TableComposable
|
||||||
|
aria-label={dataListLabel}
|
||||||
|
onSelectDataListItem={id => this.handleListItemSelect(id)}
|
||||||
|
>
|
||||||
|
{headerRow}
|
||||||
|
<Tbody>{items.map(renderRow)}</Tbody>
|
||||||
|
</TableComposable>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ToolbarPagination = (
|
||||||
|
<Pagination
|
||||||
|
isCompact
|
||||||
|
dropDirection="down"
|
||||||
|
itemCount={itemCount}
|
||||||
|
page={queryParams.page || 1}
|
||||||
|
perPage={queryParams.page_size}
|
||||||
|
perPageOptions={
|
||||||
|
showPageSizeOptions
|
||||||
|
? [
|
||||||
|
{ title: '5', value: 5 },
|
||||||
|
{ title: '10', value: 10 },
|
||||||
|
{ title: '20', value: 20 },
|
||||||
|
{ title: '50', value: 50 },
|
||||||
|
]
|
||||||
|
: []
|
||||||
|
}
|
||||||
|
onSetPage={this.handleSetPage}
|
||||||
|
onPerPageSelect={this.handleSetPageSize}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<ListHeader
|
||||||
|
itemCount={itemCount}
|
||||||
|
renderToolbar={renderToolbar}
|
||||||
|
emptyStateControls={emptyStateControls}
|
||||||
|
searchColumns={searchColumns}
|
||||||
|
sortColumns={sortColumns}
|
||||||
|
searchableKeys={toolbarSearchableKeys}
|
||||||
|
relatedSearchableKeys={toolbarRelatedSearchableKeys}
|
||||||
|
qsConfig={qsConfig}
|
||||||
|
pagination={ToolbarPagination}
|
||||||
|
/>
|
||||||
|
{Content}
|
||||||
|
{items.length ? (
|
||||||
|
<Pagination
|
||||||
|
variant="bottom"
|
||||||
|
itemCount={itemCount}
|
||||||
|
page={queryParams.page || 1}
|
||||||
|
perPage={queryParams.page_size}
|
||||||
|
perPageOptions={
|
||||||
|
showPageSizeOptions
|
||||||
|
? [
|
||||||
|
{ title: '5', value: 5 },
|
||||||
|
{ title: '10', value: 10 },
|
||||||
|
{ title: '20', value: 20 },
|
||||||
|
{ title: '50', value: 50 },
|
||||||
|
]
|
||||||
|
: []
|
||||||
|
}
|
||||||
|
onSetPage={this.handleSetPage}
|
||||||
|
onPerPageSelect={this.handleSetPageSize}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Item = PropTypes.shape({
|
||||||
|
id: PropTypes.number.isRequired,
|
||||||
|
url: PropTypes.string.isRequired,
|
||||||
|
name: PropTypes.string,
|
||||||
|
});
|
||||||
|
|
||||||
|
PaginatedTable.propTypes = {
|
||||||
|
items: PropTypes.arrayOf(Item).isRequired,
|
||||||
|
itemCount: PropTypes.number.isRequired,
|
||||||
|
pluralizedItemName: PropTypes.string,
|
||||||
|
qsConfig: QSConfig.isRequired,
|
||||||
|
renderRow: PropTypes.func,
|
||||||
|
toolbarSearchColumns: SearchColumns,
|
||||||
|
toolbarSearchableKeys: PropTypes.arrayOf(PropTypes.string),
|
||||||
|
toolbarRelatedSearchableKeys: PropTypes.arrayOf(PropTypes.string),
|
||||||
|
toolbarSortColumns: SortColumns,
|
||||||
|
showPageSizeOptions: PropTypes.bool,
|
||||||
|
renderToolbar: PropTypes.func,
|
||||||
|
hasContentLoading: PropTypes.bool,
|
||||||
|
contentError: PropTypes.shape(),
|
||||||
|
onRowClick: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
PaginatedTable.defaultProps = {
|
||||||
|
hasContentLoading: false,
|
||||||
|
contentError: null,
|
||||||
|
toolbarSearchColumns: [],
|
||||||
|
toolbarSearchableKeys: [],
|
||||||
|
toolbarRelatedSearchableKeys: [],
|
||||||
|
toolbarSortColumns: [],
|
||||||
|
pluralizedItemName: 'Items',
|
||||||
|
showPageSizeOptions: true,
|
||||||
|
renderRow: item => <PaginatedTableRow key={item.id} item={item} />,
|
||||||
|
renderToolbar: props => <DataListToolbar {...props} />,
|
||||||
|
onRowClick: () => null,
|
||||||
|
};
|
||||||
|
|
||||||
|
export { PaginatedTable as _PaginatedTable };
|
||||||
|
export default withI18n()(withRouter(PaginatedTable));
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import {
|
||||||
|
DataListItem,
|
||||||
|
DataListItemRow,
|
||||||
|
DataListItemCells,
|
||||||
|
TextContent,
|
||||||
|
} from '@patternfly/react-core';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import DataListCell from '../DataListCell';
|
||||||
|
|
||||||
|
const DetailWrapper = styled(TextContent)`
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns:
|
||||||
|
minmax(70px, max-content)
|
||||||
|
repeat(auto-fit, minmax(60px, max-content));
|
||||||
|
grid-gap: 10px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default function PaginatedDataListItem({ item }) {
|
||||||
|
return (
|
||||||
|
<DataListItem
|
||||||
|
aria-labelledby={`items-list-item-${item.id}`}
|
||||||
|
key={item.id}
|
||||||
|
id={`${item.id}`}
|
||||||
|
>
|
||||||
|
<DataListItemRow>
|
||||||
|
<DataListItemCells
|
||||||
|
dataListCells={[
|
||||||
|
<DataListCell key="name">
|
||||||
|
<DetailWrapper>
|
||||||
|
<Link to={{ pathname: item.url }}>
|
||||||
|
<b id={`items-list-item-${item.id}`}>{item.name}</b>
|
||||||
|
</Link>
|
||||||
|
</DetailWrapper>
|
||||||
|
</DataListCell>,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</DataListItemRow>
|
||||||
|
</DataListItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
5
awx/ui_next/src/components/PaginatedTable/index.js
Normal file
5
awx/ui_next/src/components/PaginatedTable/index.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export { default } from './PaginatedTable';
|
||||||
|
export { default as PaginatedTableRow } from './PaginatedTableRow';
|
||||||
|
export { default as ActionsTd } from './ActionsTd';
|
||||||
|
// export { default as ToolbarDeleteButton } from './ToolbarDeleteButton';
|
||||||
|
// export { default as ToolbarAddButton } from './ToolbarAddButton';
|
||||||
@@ -3,16 +3,18 @@ import { useLocation, useRouteMatch } from 'react-router-dom';
|
|||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Card, PageSection } from '@patternfly/react-core';
|
import { Card, PageSection } from '@patternfly/react-core';
|
||||||
|
import { Thead, Tr, Th } from '@patternfly/react-table';
|
||||||
|
|
||||||
import { OrganizationsAPI } from '../../../api';
|
import { OrganizationsAPI } from '../../../api';
|
||||||
import useRequest, { useDeleteItems } from '../../../util/useRequest';
|
import useRequest, { useDeleteItems } from '../../../util/useRequest';
|
||||||
import AlertModal from '../../../components/AlertModal';
|
import AlertModal from '../../../components/AlertModal';
|
||||||
import DataListToolbar from '../../../components/DataListToolbar';
|
import DataListToolbar from '../../../components/DataListToolbar';
|
||||||
import ErrorDetail from '../../../components/ErrorDetail';
|
import ErrorDetail from '../../../components/ErrorDetail';
|
||||||
import PaginatedDataList, {
|
import {
|
||||||
ToolbarAddButton,
|
ToolbarAddButton,
|
||||||
ToolbarDeleteButton,
|
ToolbarDeleteButton,
|
||||||
} from '../../../components/PaginatedDataList';
|
} from '../../../components/PaginatedDataList';
|
||||||
|
import PaginatedTable from '../../../components/PaginatedTable';
|
||||||
import { getQSConfig, parseQueryString } from '../../../util/qs';
|
import { getQSConfig, parseQueryString } from '../../../util/qs';
|
||||||
import OrganizationListItem from './OrganizationListItem';
|
import OrganizationListItem from './OrganizationListItem';
|
||||||
|
|
||||||
@@ -117,7 +119,7 @@ function OrganizationsList({ i18n }) {
|
|||||||
<>
|
<>
|
||||||
<PageSection>
|
<PageSection>
|
||||||
<Card>
|
<Card>
|
||||||
<PaginatedDataList
|
<PaginatedTable
|
||||||
contentError={contentError}
|
contentError={contentError}
|
||||||
hasContentLoading={hasContentLoading}
|
hasContentLoading={hasContentLoading}
|
||||||
items={organizations}
|
items={organizations}
|
||||||
@@ -152,12 +154,27 @@ function OrganizationsList({ i18n }) {
|
|||||||
]}
|
]}
|
||||||
toolbarSearchableKeys={searchableKeys}
|
toolbarSearchableKeys={searchableKeys}
|
||||||
toolbarRelatedSearchableKeys={relatedSearchableKeys}
|
toolbarRelatedSearchableKeys={relatedSearchableKeys}
|
||||||
|
headerRow={
|
||||||
|
<Thead>
|
||||||
|
<Tr>
|
||||||
|
<Th
|
||||||
|
select={{
|
||||||
|
onSelect: handleSelectAll,
|
||||||
|
isSelected: isAllSelected,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Th>{i18n._(t`Name`)}</Th>
|
||||||
|
<Th>{i18n._(t`Members`)}</Th>
|
||||||
|
<Th>{i18n._(t`Teams`)}</Th>
|
||||||
|
</Tr>
|
||||||
|
</Thead>
|
||||||
|
}
|
||||||
renderToolbar={props => (
|
renderToolbar={props => (
|
||||||
<DataListToolbar
|
<DataListToolbar
|
||||||
{...props}
|
{...props}
|
||||||
showSelectAll
|
// showSelectAll
|
||||||
isAllSelected={isAllSelected}
|
// isAllSelected={isAllSelected}
|
||||||
onSelectAll={handleSelectAll}
|
// onSelectAll={handleSelectAll}
|
||||||
qsConfig={QS_CONFIG}
|
qsConfig={QS_CONFIG}
|
||||||
additionalControls={[
|
additionalControls={[
|
||||||
...(canAdd
|
...(canAdd
|
||||||
@@ -172,10 +189,11 @@ function OrganizationsList({ i18n }) {
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
renderItem={o => (
|
renderRow={(o, index) => (
|
||||||
<OrganizationListItem
|
<OrganizationListItem
|
||||||
key={o.id}
|
key={o.id}
|
||||||
organization={o}
|
organization={o}
|
||||||
|
rowIndex={index}
|
||||||
detailUrl={`${match.url}/${o.id}`}
|
detailUrl={`${match.url}/${o.id}`}
|
||||||
isSelected={selected.some(row => row.id === o.id)}
|
isSelected={selected.some(row => row.id === o.id)}
|
||||||
onSelect={() => handleSelect(o)}
|
onSelect={() => handleSelect(o)}
|
||||||
|
|||||||
@@ -2,105 +2,57 @@ import React from 'react';
|
|||||||
import { string, bool, func } from 'prop-types';
|
import { string, bool, func } from 'prop-types';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import {
|
import { Button, Tooltip } from '@patternfly/react-core';
|
||||||
Badge as PFBadge,
|
import { Tr, Td } from '@patternfly/react-table';
|
||||||
Button,
|
|
||||||
DataListAction as _DataListAction,
|
|
||||||
DataListCheck,
|
|
||||||
DataListItem,
|
|
||||||
DataListItemCells,
|
|
||||||
DataListItemRow,
|
|
||||||
Tooltip,
|
|
||||||
} from '@patternfly/react-core';
|
|
||||||
|
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import styled from 'styled-components';
|
|
||||||
import { PencilAltIcon } from '@patternfly/react-icons';
|
import { PencilAltIcon } from '@patternfly/react-icons';
|
||||||
import DataListCell from '../../../components/DataListCell';
|
import { ActionsTd } from '../../../components/PaginatedTable';
|
||||||
|
|
||||||
import { Organization } from '../../../types';
|
import { Organization } from '../../../types';
|
||||||
|
|
||||||
const Badge = styled(PFBadge)`
|
|
||||||
margin-left: 8px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const ListGroup = styled.span`
|
|
||||||
margin-left: 24px;
|
|
||||||
|
|
||||||
&:first-of-type {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const DataListAction = styled(_DataListAction)`
|
|
||||||
align-items: center;
|
|
||||||
display: grid;
|
|
||||||
grid-gap: 16px;
|
|
||||||
grid-template-columns: 40px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
function OrganizationListItem({
|
function OrganizationListItem({
|
||||||
organization,
|
organization,
|
||||||
isSelected,
|
isSelected,
|
||||||
onSelect,
|
onSelect,
|
||||||
|
rowIndex,
|
||||||
detailUrl,
|
detailUrl,
|
||||||
i18n,
|
i18n,
|
||||||
}) {
|
}) {
|
||||||
const labelId = `check-action-${organization.id}`;
|
const labelId = `check-action-${organization.id}`;
|
||||||
return (
|
return (
|
||||||
<DataListItem
|
<Tr id={`${organization.id}`}>
|
||||||
key={organization.id}
|
<Td
|
||||||
aria-labelledby={labelId}
|
select={{
|
||||||
id={`${organization.id}`}
|
rowIndex,
|
||||||
>
|
isSelected,
|
||||||
<DataListItemRow>
|
onSelect,
|
||||||
<DataListCheck
|
disable: false,
|
||||||
id={`select-organization-${organization.id}`}
|
}}
|
||||||
checked={isSelected}
|
/>
|
||||||
onChange={onSelect}
|
<Td id={labelId}>
|
||||||
aria-labelledby={labelId}
|
<Link to={`${detailUrl}`}>
|
||||||
/>
|
<b>{organization.name}</b>
|
||||||
<DataListItemCells
|
</Link>
|
||||||
dataListCells={[
|
</Td>
|
||||||
<DataListCell key="name" id={labelId}>
|
<Td>{organization.summary_fields.related_field_counts.users}</Td>
|
||||||
<Link to={`${detailUrl}`}>
|
<Td>{organization.summary_fields.related_field_counts.teams}</Td>
|
||||||
<b>{organization.name}</b>
|
<ActionsTd numActions={2}>
|
||||||
</Link>
|
{organization.summary_fields.user_capabilities.edit ? (
|
||||||
</DataListCell>,
|
<Tooltip content={i18n._(t`Edit Organization`)} position="top">
|
||||||
<DataListCell key="related-field-counts">
|
<Button
|
||||||
<ListGroup>
|
aria-label={i18n._(t`Edit Organization`)}
|
||||||
{i18n._(t`Members`)}
|
variant="plain"
|
||||||
<Badge isRead>
|
component={Link}
|
||||||
{organization.summary_fields.related_field_counts.users}
|
to={`/organizations/${organization.id}/edit`}
|
||||||
</Badge>
|
>
|
||||||
</ListGroup>
|
<PencilAltIcon />
|
||||||
<ListGroup>
|
</Button>
|
||||||
{i18n._(t`Teams`)}
|
</Tooltip>
|
||||||
<Badge isRead>
|
) : (
|
||||||
{organization.summary_fields.related_field_counts.teams}
|
''
|
||||||
</Badge>
|
)}
|
||||||
</ListGroup>
|
</ActionsTd>
|
||||||
</DataListCell>,
|
</Tr>
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<DataListAction aria-label="actions" aria-labelledby={labelId}>
|
|
||||||
{organization.summary_fields.user_capabilities.edit ? (
|
|
||||||
<Tooltip content={i18n._(t`Edit Organization`)} position="top">
|
|
||||||
<Button
|
|
||||||
aria-label={i18n._(t`Edit Organization`)}
|
|
||||||
variant="plain"
|
|
||||||
component={Link}
|
|
||||||
to={`/organizations/${organization.id}/edit`}
|
|
||||||
>
|
|
||||||
<PencilAltIcon />
|
|
||||||
</Button>
|
|
||||||
</Tooltip>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
</DataListAction>
|
|
||||||
</DataListItemRow>
|
|
||||||
</DataListItem>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user