Tables replacement in mcs (#74)

Replaced all the tables in mcs to be consistent with the new design

Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
Alex
2020-04-21 17:38:46 -05:00
committed by GitHub
parent 5fa0a0fca8
commit d9c212fe2f
15 changed files with 530 additions and 693 deletions

File diff suppressed because one or more lines are too long

View File

@@ -16,34 +16,20 @@
import React from "react"; import React from "react";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles"; import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import Paper from "@material-ui/core/Paper";
import Grid from "@material-ui/core/Grid"; import Grid from "@material-ui/core/Grid";
import api from "../../../../common/api"; import { Button } from "@material-ui/core";
import { Bucket, BucketList } from "../types";
import {
Button,
IconButton,
LinearProgress,
TableFooter,
TablePagination
} from "@material-ui/core";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import DeleteIcon from "@material-ui/icons/Delete";
import AddBucket from "./AddBucket";
import DeleteBucket from "./DeleteBucket";
import { MinTablePaginationActions } from "../../../../common/MinTablePaginationActions";
import { CreateIcon } from "../../../../icons";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
import InputAdornment from "@material-ui/core/InputAdornment"; import InputAdornment from "@material-ui/core/InputAdornment";
import SearchIcon from "@material-ui/icons/Search"; import SearchIcon from "@material-ui/icons/Search";
import Moment from "react-moment"; import Moment from "react-moment";
import { Link } from "react-router-dom"; import api from "../../../../common/api";
import ViewIcon from "@material-ui/icons/Visibility"; import { Bucket, BucketList } from "../types";
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
import AddBucket from "./AddBucket";
import DeleteBucket from "./DeleteBucket";
import { MinTablePaginationActions } from "../../../../common/MinTablePaginationActions";
import { CreateIcon } from "../../../../icons";
const styles = (theme: Theme) => const styles = (theme: Theme) =>
createStyles({ createStyles({
@@ -134,8 +120,8 @@ class ListBuckets extends React.Component<
.then((res: BucketList) => { .then((res: BucketList) => {
this.setState({ this.setState({
loading: false, loading: false,
records: res.buckets, records: res.buckets || [],
totalRecords: res.total, totalRecords: !res.buckets ? 0 : res.total,
error: "" error: ""
}); });
// if we get 0 results, and page > 0 , go down 1 page // if we get 0 results, and page > 0 , go down 1 page
@@ -208,6 +194,29 @@ class ListBuckets extends React.Component<
this.setState({ deleteOpen: true, selectedBucket: bucket }); this.setState({ deleteOpen: true, selectedBucket: bucket });
}; };
const tableActions = [
{ type: "view", to: `/buckets`, sendOnlyId: true },
{ type: "delete", onClick: confirmDeleteBucket, sendOnlyId: true }
];
const displayParsedDate = (date: string) => {
return <Moment>{date}</Moment>;
};
const filteredRecords = records
.slice(offset, offset + rowsPerPage)
.filter((b: Bucket) => {
if (filterBuckets === "") {
return true;
} else {
if (b.name.indexOf(filterBuckets) >= 0) {
return true;
} else {
return false;
}
}
});
return ( return (
<React.Fragment> <React.Fragment>
{addScreenOpen && ( {addScreenOpen && (
@@ -271,78 +280,35 @@ class ListBuckets extends React.Component<
<br /> <br />
</Grid> </Grid>
<Grid item xs={12}> <Grid item xs={12}>
<Paper className={classes.paper}> <TableWrapper
{loading && <LinearProgress />} itemActions={tableActions}
{records != null && records.length > 0 ? ( columns={[
<Table size="medium"> { label: "Name", elementKey: "name" },
<TableHead className={classes.minTableHeader}> {
<TableRow> label: "Creation Date",
<TableCell>Name</TableCell> elementKey: "creation_date",
<TableCell>Creation Date</TableCell> renderFunction: displayParsedDate
<TableCell align="right">Actions</TableCell> }
</TableRow> ]}
</TableHead> isLoading={loading}
<TableBody> records={filteredRecords}
{records entityName="Buckets"
.slice(offset, offset + rowsPerPage) idField="name"
.filter((b: Bucket) => { paginatorConfig={{
if (filterBuckets === "") { rowsPerPageOptions: [5, 10, 25],
return true; colSpan: 3,
} else { count: totalRecords,
if (b.name.indexOf(filterBuckets) >= 0) { rowsPerPage: rowsPerPage,
return true; page: page,
} else { SelectProps: {
return false; inputProps: { "aria-label": "rows per page" },
} native: true
} },
}) onChangePage: handleChangePage,
.map(row => ( onChangeRowsPerPage: handleChangeRowsPerPage,
<TableRow key={row.name}> ActionsComponent: MinTablePaginationActions
<TableCell>{row.name}</TableCell> }}
<TableCell> />
<Moment>{row.creation_date}</Moment>
</TableCell>
<TableCell align="right">
<Link to={`/buckets/${row.name}`}>
<IconButton aria-label="delete">
<ViewIcon />
</IconButton>
</Link>
<IconButton
aria-label="delete"
onClick={() => {
confirmDeleteBucket(row.name);
}}
>
<DeleteIcon />
</IconButton>
</TableCell>
</TableRow>
))}
</TableBody>
<TableFooter>
<TableRow>
<TablePagination
rowsPerPageOptions={[5, 10, 25]}
colSpan={3}
count={totalRecords}
rowsPerPage={rowsPerPage}
page={page}
SelectProps={{
inputProps: { "aria-label": "rows per page" },
native: true
}}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
ActionsComponent={MinTablePaginationActions}
/>
</TableRow>
</TableFooter>
</Table>
) : (
<div>No Buckets</div>
)}
</Paper>
</Grid> </Grid>
</Grid> </Grid>
</React.Fragment> </React.Fragment>

View File

@@ -67,6 +67,7 @@ class DeleteEvent extends React.Component<
if (bucketEvent == null) { if (bucketEvent == null) {
return; return;
} }
this.setState({ deleteLoading: true }, () => { this.setState({ deleteLoading: true }, () => {
api api
.invoke( .invoke(

View File

@@ -16,29 +16,17 @@
import React from "react"; import React from "react";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles"; import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import Paper from "@material-ui/core/Paper";
import Grid from "@material-ui/core/Grid"; import Grid from "@material-ui/core/Grid";
import api from "../../../../common/api"; import api from "../../../../common/api";
import { BucketEvent, BucketEventList, BucketInfo } from "../types"; import { BucketEvent, BucketEventList, BucketInfo } from "../types";
import { import { Button } from "@material-ui/core";
Button,
IconButton,
LinearProgress,
TableFooter,
TablePagination
} from "@material-ui/core";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import DeleteIcon from "@material-ui/icons/Delete";
import SetAccessPolicy from "./SetAccessPolicy"; import SetAccessPolicy from "./SetAccessPolicy";
import { MinTablePaginationActions } from "../../../../common/MinTablePaginationActions"; import { MinTablePaginationActions } from "../../../../common/MinTablePaginationActions";
import { CreateIcon } from "../../../../icons"; import { CreateIcon } from "../../../../icons";
import AddEvent from "./AddEvent"; import AddEvent from "./AddEvent";
import DeleteEvent from "./DeleteEvent"; import DeleteEvent from "./DeleteEvent";
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
const styles = (theme: Theme) => const styles = (theme: Theme) =>
createStyles({ createStyles({
@@ -135,19 +123,16 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
api api
.invoke("GET", `/api/v1/buckets/${bucketName}/events`) .invoke("GET", `/api/v1/buckets/${bucketName}/events`)
.then((res: BucketEventList) => { .then((res: BucketEventList) => {
const events = res.events;
this.setState({ this.setState({
loading: false, loading: false,
records: res.events, records: events || [],
totalRecords: res.total, totalRecords: res.total,
error: "" error: ""
}); });
// if we get 0 results, and page > 0 , go down 1 page // if we get 0 results, and page > 0 , go down 1 page
if ( if ((!events || res.events.length === 0) && page > 0) {
(res.events === undefined ||
res.events == null ||
res.events.length === 0) &&
page > 0
) {
const newPage = page - 1; const newPage = page - 1;
this.setState({ page: newPage }, () => { this.setState({ page: newPage }, () => {
this.fetchEvents(); this.fetchEvents();
@@ -231,6 +216,14 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
accessPolicy = info.access; accessPolicy = info.access;
} }
const eventsDisplay = (events: string[]) => {
return <React.Fragment>{events.join(", ")}</React.Fragment>;
};
const tableActions = [{ type: "delete", onClick: confirmDeleteEvent }];
const filteredRecords = records.slice(offset, offset + rowsPerPage);
return ( return (
<React.Fragment> <React.Fragment>
<AddEvent <AddEvent
@@ -301,66 +294,37 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
<br /> <br />
</Grid> </Grid>
<Grid item xs={12}> <Grid item xs={12}>
<Paper className={classes.paper}> <TableWrapper
{loading && <LinearProgress />} itemActions={tableActions}
{records != null && records.length > 0 ? ( columns={[
<Table size="medium"> { label: "SQS", elementKey: "arn" },
<TableHead className={classes.minTableHeader}> {
<TableRow> label: "Events",
<TableCell>SQS</TableCell> elementKey: "events",
<TableCell>Events</TableCell> renderFunction: eventsDisplay
<TableCell>Prefix</TableCell> },
<TableCell>Suffix</TableCell> { label: "Prefix", elementKey: "prefix" },
<TableCell align="right">Actions</TableCell> { label: "Suffix", elementKey: "suffix" }
</TableRow> ]}
</TableHead> isLoading={loading}
<TableBody> records={filteredRecords}
{records entityName="Events"
.slice(offset, offset + rowsPerPage) idField="id"
.map((row, index) => ( paginatorConfig={{
<TableRow rowsPerPageOptions: [5, 10, 25],
key={`bucket-evt-${row.id}-${index.toString()}`} colSpan: 3,
> count: totalRecords,
<TableCell>{row.arn}</TableCell> rowsPerPage: rowsPerPage,
<TableCell>{row.events.join(", ")}</TableCell> page: page,
<TableCell>{row.prefix}</TableCell> SelectProps: {
<TableCell>{row.suffix}</TableCell> inputProps: { "aria-label": "rows per page" },
<TableCell align="right"> native: true
<IconButton },
aria-label="delete" onChangePage: handleChangePage,
onClick={() => { onChangeRowsPerPage: handleChangeRowsPerPage,
confirmDeleteEvent(row); ActionsComponent: MinTablePaginationActions
}} }}
> />
<DeleteIcon />
</IconButton>
</TableCell>
</TableRow>
))}
</TableBody>
<TableFooter>
<TableRow>
<TablePagination
rowsPerPageOptions={[5, 10, 25]}
colSpan={3}
count={totalRecords}
rowsPerPage={rowsPerPage}
page={page}
SelectProps={{
inputProps: { "aria-label": "rows per page" },
native: true
}}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
ActionsComponent={MinTablePaginationActions}
/>
</TableRow>
</TableFooter>
</Table>
) : (
<div className={classes.noRecords}>No Events</div>
)}
</Paper>
</Grid> </Grid>
</Grid> </Grid>

View File

@@ -14,16 +14,21 @@
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react"; import React from "react";
import isString from "lodash/isString";
import { IconButton } from "@material-ui/core"; import { IconButton } from "@material-ui/core";
import ViewIcon from "./TableActionIcons/ViewIcon"; import ViewIcon from "./TableActionIcons/ViewIcon";
import PencilIcon from "./TableActionIcons/PencilIcon"; import PencilIcon from "./TableActionIcons/PencilIcon";
import DeleteIcon from "./TableActionIcons/DeleteIcon"; import DeleteIcon from "./TableActionIcons/DeleteIcon";
import { Link } from "react-router-dom";
interface IActionButton { interface IActionButton {
type: string; type: string;
onClick: (id: string) => any; onClick?: (id: string) => any;
to?: string;
valueToSend: any; valueToSend: any;
selected: boolean; selected: boolean;
sendOnlyId?: boolean;
idField: string;
} }
const defineIcon = (type: string, selected: boolean) => { const defineIcon = (type: string, selected: boolean) => {
@@ -43,18 +48,37 @@ const TableActionButton = ({
type, type,
onClick, onClick,
valueToSend, valueToSend,
selected idField,
selected,
to,
sendOnlyId = false
}: IActionButton) => { }: IActionButton) => {
return ( const valueClick = sendOnlyId ? valueToSend[idField] : valueToSend;
const buttonElement = (
<IconButton <IconButton
aria-label={type} aria-label={type}
onClick={() => { onClick={
onClick(valueToSend); onClick
}} ? () => {
onClick(valueClick);
}
: () => null
}
> >
{defineIcon(type, selected)} {defineIcon(type, selected)}
</IconButton> </IconButton>
); );
if (onClick) {
return buttonElement;
}
if (isString(to)) {
return <Link to={`${to}/${valueClick}`}>{buttonElement}</Link>;
}
return null;
}; };
export default TableActionButton; export default TableActionButton;

View File

@@ -15,6 +15,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react"; import React from "react";
import get from "lodash/get"; import get from "lodash/get";
import isString from "lodash/isString";
import { import {
LinearProgress, LinearProgress,
TablePagination, TablePagination,
@@ -31,15 +32,20 @@ import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { TablePaginationActionsProps } from "@material-ui/core/TablePagination/TablePaginationActions"; import { TablePaginationActionsProps } from "@material-ui/core/TablePagination/TablePaginationActions";
import TableActionButton from "./TableActionButton"; import TableActionButton from "./TableActionButton";
//Interfaces for table Items
interface ItemActions { interface ItemActions {
type: string; type: string;
onClick(valueToSend: any): any; onClick?(valueToSend: any): any;
to?: string;
sendOnlyId?: boolean;
} }
interface IColumns { interface IColumns {
label: string; label: string;
elementKey: string; elementKey: string;
sortable?: boolean; sortable?: boolean;
renderFunction?: (input: any) => any;
} }
interface IPaginatorConfig { interface IPaginatorConfig {
@@ -144,59 +150,66 @@ const styles = (theme: Theme) =>
} }
}); });
// Function that renders Title Columns
const titleColumnsMap = (columns: IColumns[]) => { const titleColumnsMap = (columns: IColumns[]) => {
const columnsList = columns.map((column: IColumns, index: number) => { return columns.map((column: IColumns, index: number) => {
return ( return (
<TableCell key={`tbCT-${column.elementKey}-${index}`}> <TableCell key={`tbCT-${column.elementKey}-${index}`}>
{column.label} {column.label}
</TableCell> </TableCell>
); );
}); });
return columnsList;
}; };
// Function that renders Rows
const rowColumnsMap = ( const rowColumnsMap = (
columns: IColumns[], columns: IColumns[],
itemData: any, itemData: any,
classes: any, classes: any,
isSelected: boolean isSelected: boolean
) => { ) => {
const rowElements = columns.map((column: IColumns, index: number) => { return columns.map((column: IColumns, index: number) => {
const itemElement = get(itemData, column.elementKey, null); const itemElement = isString(itemData)
? itemData
: get(itemData, column.elementKey, null); // If the element is just a string, we render it as it is
const renderElement = column.renderFunction
? column.renderFunction(itemElement)
: itemElement; // If render function is set, we send the value to the function.
return ( return (
<TableCell <TableCell
key={`tbRE-${column.elementKey}-${index}`} key={`tbRE-${column.elementKey}-${index}`}
className={isSelected ? classes.rowSelected : classes.rowUnselected} className={isSelected ? classes.rowSelected : classes.rowUnselected}
> >
{itemElement} {renderElement}
</TableCell> </TableCell>
); );
}); });
return rowElements;
}; };
// Function to render the action buttons
const elementActions = ( const elementActions = (
actions: ItemActions[], actions: ItemActions[],
valueToSend: any, valueToSend: any,
selected: boolean selected: boolean,
idField: string
) => { ) => {
const actionsElements = actions.map((action: ItemActions, index: number) => { return actions.map((action: ItemActions, index: number) => {
return ( return (
<TableActionButton <TableActionButton
type={action.type} type={action.type}
onClick={action.onClick} onClick={action.onClick}
to={action.to}
valueToSend={valueToSend} valueToSend={valueToSend}
selected={selected} selected={selected}
key={`actions-${action.type}-${index.toString()}`} key={`actions-${action.type}-${index.toString()}`}
idField={idField}
sendOnlyId={!!action.sendOnlyId}
/> />
); );
}); });
return actionsElements;
}; };
// Main function to render the Table Wrapper
const TableWrapper = ({ const TableWrapper = ({
itemActions, itemActions,
columns, columns,
@@ -241,8 +254,11 @@ const TableWrapper = ({
<TableBody> <TableBody>
{records.map((record: any, index: number) => { {records.map((record: any, index: number) => {
const isSelected = selectedItems const isSelected = selectedItems
? selectedItems.includes(record[idField]) ? selectedItems.includes(
isString(record) ? record : record[idField]
)
: false; : false;
return ( return (
<TableRow key={`tb-${entityName}-${index.toString()}`}> <TableRow key={`tb-${entityName}-${index.toString()}`}>
{onSelect && selectedItems && ( {onSelect && selectedItems && (
@@ -252,7 +268,7 @@ const TableWrapper = ({
className={classes.checkBoxRow} className={classes.checkBoxRow}
> >
<Checkbox <Checkbox
value={record[idField]} value={isString(record) ? record : record[idField]}
color="primary" color="primary"
inputProps={{ "aria-label": "secondary checkbox" }} inputProps={{ "aria-label": "secondary checkbox" }}
checked={isSelected} checked={isSelected}
@@ -265,10 +281,15 @@ const TableWrapper = ({
{rowColumnsMap(columns, record, classes, isSelected)} {rowColumnsMap(columns, record, classes, isSelected)}
{itemActions && itemActions.length > 0 && ( {itemActions && itemActions.length > 0 && (
<TableCell <TableCell
align="right" align="center"
className={classes.actionsContainer} className={classes.actionsContainer}
> >
{elementActions(itemActions, record, isSelected)} {elementActions(
itemActions,
record,
isSelected,
idField
)}
</TableCell> </TableCell>
)} )}
</TableRow> </TableRow>

View File

@@ -21,21 +21,7 @@ import Typography from "@material-ui/core/Typography";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
import InputAdornment from "@material-ui/core/InputAdornment"; import InputAdornment from "@material-ui/core/InputAdornment";
import SearchIcon from "@material-ui/icons/Search"; import SearchIcon from "@material-ui/icons/Search";
import { import { Button } from "@material-ui/core";
Button,
IconButton,
LinearProgress,
TableFooter,
TablePagination
} from "@material-ui/core";
import Paper from "@material-ui/core/Paper";
import Table from "@material-ui/core/Table";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import TableCell from "@material-ui/core/TableCell";
import TableBody from "@material-ui/core/TableBody";
import ViewIcon from "@material-ui/icons/Visibility";
import DeleteIcon from "@material-ui/icons/Delete";
import { CreateIcon } from "../../../icons"; import { CreateIcon } from "../../../icons";
import api from "../../../common/api"; import api from "../../../common/api";
import { MinTablePaginationActions } from "../../../common/MinTablePaginationActions"; import { MinTablePaginationActions } from "../../../common/MinTablePaginationActions";
@@ -43,6 +29,7 @@ import { GroupsList } from "./types";
import { groupsSort } from "../../../utils/sortFunctions"; import { groupsSort } from "../../../utils/sortFunctions";
import AddGroup from "../Groups/AddGroup"; import AddGroup from "../Groups/AddGroup";
import DeleteGroup from "./DeleteGroup"; import DeleteGroup from "./DeleteGroup";
import TableWrapper from "../Common/TableWrapper/TableWrapper";
interface IGroupsProps { interface IGroupsProps {
classes: any; classes: any;
@@ -142,10 +129,7 @@ const Groups = ({ classes }: IGroupsProps) => {
resGroups = res.groups.sort(groupsSort); resGroups = res.groups.sort(groupsSort);
} }
setRecords(resGroups); setRecords(resGroups);
let total = 0; const total = !res.total ? 0 : res.total;
if (res.total !== null) {
total = res.total;
}
setTotalRecords(total); setTotalRecords(total);
setError(""); setError("");
isLoading(false); isLoading(false);
@@ -182,6 +166,21 @@ const Groups = ({ classes }: IGroupsProps) => {
elementItem.includes(filter) elementItem.includes(filter)
); );
const viewAction = (group: any) => {
setGroupOpen(true);
setSelectedGroup(group);
};
const deleteAction = (group: any) => {
setDeleteOpen(true);
setSelectedGroup(group);
};
const tableActions = [
{ type: "view", onClick: viewAction },
{ type: "delete", onClick: deleteAction }
];
return ( return (
<React.Fragment> <React.Fragment>
{addGroupOpen && ( {addGroupOpen && (
@@ -241,68 +240,28 @@ const Groups = ({ classes }: IGroupsProps) => {
<br /> <br />
</Grid> </Grid>
<Grid item xs={12}> <Grid item xs={12}>
<Paper className={classes.paper}> <TableWrapper
{loading && <LinearProgress />} itemActions={tableActions}
{records != null && records.length > 0 ? ( columns={[{ label: "Name", elementKey: "" }]}
<Table size="medium"> isLoading={loading}
<TableHead className={classes.minTableHeader}> records={filteredRecords}
<TableRow> entityName="Groups"
<TableCell>Name</TableCell> idField=""
<TableCell align="right">Actions</TableCell> paginatorConfig={{
</TableRow> rowsPerPageOptions: [5, 10, 25],
</TableHead> colSpan: 3,
<TableBody> count: totalRecords,
{filteredRecords.map(group => ( rowsPerPage: rowsPerPage,
<TableRow key={`user-${group}`}> page: page,
<TableCell className={classes.wrapCell}> SelectProps: {
{group} inputProps: { "aria-label": "rows per page" },
</TableCell> native: true
<TableCell align="right"> },
<IconButton onChangePage: handleChangePage,
aria-label="view" onChangeRowsPerPage: handleChangeRowsPerPage,
onClick={() => { ActionsComponent: MinTablePaginationActions
setGroupOpen(true); }}
setSelectedGroup(group); />
}}
>
<ViewIcon />
</IconButton>
<IconButton
aria-label="delete"
onClick={() => {
setDeleteOpen(true);
setSelectedGroup(group);
}}
>
<DeleteIcon />
</IconButton>
</TableCell>
</TableRow>
))}
</TableBody>
<TableFooter>
<TableRow>
<TablePagination
rowsPerPageOptions={[5, 10, 25]}
colSpan={3}
count={totalRecords}
rowsPerPage={rowsPerPage}
page={page}
SelectProps={{
inputProps: { "aria-label": "rows per page" },
native: true
}}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
ActionsComponent={MinTablePaginationActions}
/>
</TableRow>
</TableFooter>
</Table>
) : (
<div>No Groups Available</div>
)}
</Paper>
</Grid> </Grid>
</Grid> </Grid>
</React.Fragment> </React.Fragment>

View File

@@ -18,22 +18,16 @@ import React, { useState, useEffect } from "react";
import get from "lodash/get"; import get from "lodash/get";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles"; import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { LinearProgress } from "@material-ui/core"; import { LinearProgress } from "@material-ui/core";
import TableContainer from "@material-ui/core/TableContainer";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import Paper from "@material-ui/core/Paper"; import Paper from "@material-ui/core/Paper";
import Grid from "@material-ui/core/Grid"; import Grid from "@material-ui/core/Grid";
import Title from "../../../common/Title"; import Title from "../../../common/Title";
import Checkbox from "@material-ui/core/Checkbox";
import { UsersList } from "../Users/types"; import { UsersList } from "../Users/types";
import { usersSort } from "../../../utils/sortFunctions"; import { usersSort } from "../../../utils/sortFunctions";
import api from "../../../common/api"; import api from "../../../common/api";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
import InputAdornment from "@material-ui/core/InputAdornment"; import InputAdornment from "@material-ui/core/InputAdornment";
import SearchIcon from "@material-ui/icons/Search"; import SearchIcon from "@material-ui/icons/Search";
import TableWrapper from "../Common/TableWrapper/TableWrapper";
interface IGroupsProps { interface IGroupsProps {
classes: any; classes: any;
@@ -198,40 +192,17 @@ const UsersSelectors = ({
}} }}
/> />
</Grid> </Grid>
<TableContainer className={classes.tableContainer}> <Grid item xs={12}>
<Table size="small" stickyHeader aria-label="sticky table"> <TableWrapper
<TableHead className={classes.minTableHeader}> columns={[{ label: "Access Key", elementKey: "accessKey" }]}
<TableRow> onSelect={selectionChanged}
<TableCell className={classes.stickyHeader}> selectedItems={selUsers}
Select isLoading={loading}
</TableCell> records={filteredRecords}
<TableCell className={classes.stickyHeader}> entityName="Users"
Access Key idField="accessKey"
</TableCell> />
</TableRow> </Grid>
</TableHead>
<TableBody>
{filteredRecords.map(row => (
<TableRow key={`group-${row.accessKey}`}>
<TableCell padding="checkbox">
<Checkbox
value={row.accessKey}
color="primary"
inputProps={{
"aria-label": "secondary checkbox"
}}
onChange={selectionChanged}
checked={selUsers.includes(row.accessKey)}
/>
</TableCell>
<TableCell className={classes.wrapCell}>
{row.accessKey}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</React.Fragment> </React.Fragment>
) : ( ) : (
<div className={classes.noFound}>No Users Available</div> <div className={classes.noFound}>No Users Available</div>

View File

@@ -16,33 +16,24 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles"; import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { import { TextField } from "@material-ui/core";
Button, import { red } from "@material-ui/core/colors";
IconButton,
LinearProgress,
TableFooter,
TablePagination,
TextField
} from "@material-ui/core";
import Grid from "@material-ui/core/Grid"; import Grid from "@material-ui/core/Grid";
import Button from "@material-ui/core/Button";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import InputAdornment from "@material-ui/core/InputAdornment"; import InputAdornment from "@material-ui/core/InputAdornment";
import SearchIcon from "@material-ui/icons/Search"; import SearchIcon from "@material-ui/icons/Search";
import { CreateIcon } from "../../../icons";
import Paper from "@material-ui/core/Paper";
import Table from "@material-ui/core/Table";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import TableCell from "@material-ui/core/TableCell";
import TableBody from "@material-ui/core/TableBody";
import { Link } from "react-router-dom";
import ViewIcon from "@material-ui/icons/Visibility";
import DeleteIcon from "@material-ui/icons/Delete";
import { MinTablePaginationActions } from "../../../common/MinTablePaginationActions"; import { MinTablePaginationActions } from "../../../common/MinTablePaginationActions";
import { NotificationEndpointItem, NotificationEndpointsList } from "./types"; import {
NotificationEndpointItem,
NotificationEndpointsList,
TransformedEndpointItem
} from "./types";
import { notificationTransform } from "./utils";
import { CreateIcon } from "../../../icons";
import api from "../../../common/api"; import api from "../../../common/api";
import FiberManualRecordIcon from "@material-ui/icons/FiberManualRecord"; import FiberManualRecordIcon from "@material-ui/icons/FiberManualRecord";
import { red } from "@material-ui/core/colors"; import TableWrapper from "../Common/TableWrapper/TableWrapper";
import AddNotificationEndpoint from "./AddNotificationEndpoint"; import AddNotificationEndpoint from "./AddNotificationEndpoint";
interface IListNotificationEndpoints { interface IListNotificationEndpoints {
@@ -79,7 +70,7 @@ const styles = (theme: Theme) =>
const ListNotificationEndpoints = ({ classes }: IListNotificationEndpoints) => { const ListNotificationEndpoints = ({ classes }: IListNotificationEndpoints) => {
//Local States //Local States
const [records, setRecords] = useState<NotificationEndpointItem[]>([]); const [records, setRecords] = useState<TransformedEndpointItem[]>([]);
const [totalRecords, setTotalRecords] = useState<number>(0); const [totalRecords, setTotalRecords] = useState<number>(0);
const [rowsPerPage, setRowsPerPage] = useState<number>(10); const [rowsPerPage, setRowsPerPage] = useState<number>(10);
const [page, setPage] = useState<number>(0); const [page, setPage] = useState<number>(0);
@@ -100,7 +91,7 @@ const ListNotificationEndpoints = ({ classes }: IListNotificationEndpoints) => {
if (res.notification_endpoints !== null) { if (res.notification_endpoints !== null) {
resNotEndList = res.notification_endpoints; resNotEndList = res.notification_endpoints;
} }
setRecords(resNotEndList); setRecords(notificationTransform(resNotEndList));
setTotalRecords(resNotEndList.length); setTotalRecords(resNotEndList.length);
setError(""); setError("");
setIsLoading(false); setIsLoading(false);
@@ -118,6 +109,44 @@ const ListNotificationEndpoints = ({ classes }: IListNotificationEndpoints) => {
setIsLoading(true); setIsLoading(true);
}, []); }, []);
const tableActions = [
{ type: "view", to: "/notification-endpoints", sendOnlyId: true },
{
type: "delete",
onClick: (row: any) => {
//confirmDeleteBucket(row.name);
}
}
];
const filteredRecords = records.filter((b: TransformedEndpointItem) => {
if (filter === "") {
return true;
} else {
if (b.service_name.indexOf(filter) >= 0) {
return true;
} else {
return false;
}
}
});
const statusDisplay = (status: string) => {
return (
<div
style={{
display: "flex",
alignItems: "center"
}}
>
<FiberManualRecordIcon
style={status === "Offline" ? { color: red[500] } : {}}
/>
{status}
</div>
);
};
return ( return (
<React.Fragment> <React.Fragment>
{addScreenOpen && ( {addScreenOpen && (
@@ -170,101 +199,42 @@ const ListNotificationEndpoints = ({ classes }: IListNotificationEndpoints) => {
<br /> <br />
</Grid> </Grid>
<Grid item xs={12}> <Grid item xs={12}>
<Paper className={classes.paper}> <TableWrapper
{isLoading && <LinearProgress />} itemActions={tableActions}
{records != null && records.length > 0 ? ( columns={[
<Table size="medium"> { label: "Service", elementKey: "service_name" },
<TableHead className={classes.minTableHeader}> {
<TableRow> label: "Status",
<TableCell>Service</TableCell> elementKey: "status",
<TableCell>Status</TableCell> renderFunction: statusDisplay
<TableCell align="right">Actions</TableCell> }
</TableRow> ]}
</TableHead> isLoading={isLoading}
<TableBody> records={filteredRecords}
{records entityName="Notification Endpoints"
.filter((b: NotificationEndpointItem) => { idField="service_name"
if (filter === "") { paginatorConfig={{
return true; rowsPerPageOptions: [5, 10, 25],
} else { colSpan: 3,
if (b.service.indexOf(filter) >= 0) { count: totalRecords,
return true; rowsPerPage: rowsPerPage,
} else { page: page,
return false; SelectProps: {
} inputProps: { "aria-label": "rows per page" },
} native: true
}) },
.map(row => ( onChangePage: (event: unknown, newPage: number) => {
<TableRow key={`${row.service}:${row.account_id}`}> setPage(newPage);
<TableCell>{`${row.service}:${row.account_id}`}</TableCell> },
{/*<TableCell>{row.account_id}</TableCell>*/} onChangeRowsPerPage: (
<TableCell className={classes.iconText}> event: React.ChangeEvent<HTMLInputElement>
<div ) => {
style={{ const rPP = parseInt(event.target.value, 10);
display: "flex", setRowsPerPage(rPP);
alignItems: "center" },
}} ActionsComponent: MinTablePaginationActions
> }}
<FiberManualRecordIcon />
style={
row.status === "Offline"
? { color: red[500] }
: {}
}
/>
{row.status}
</div>
</TableCell>
<TableCell align="right">
<Link
to={`/notification-endpoints/${row.service}:${row.account_id}`}
>
<IconButton aria-label="delete">
<ViewIcon />
</IconButton>
</Link>
<IconButton
aria-label="delete"
onClick={() => {
//confirmDeleteBucket(row.name);
}}
>
<DeleteIcon />
</IconButton>
</TableCell>
</TableRow>
))}
</TableBody>
<TableFooter>
<TableRow>
<TablePagination
rowsPerPageOptions={[5, 10, 25]}
colSpan={3}
count={totalRecords}
rowsPerPage={rowsPerPage}
page={page}
SelectProps={{
inputProps: { "aria-label": "rows per page" },
native: true
}}
onChangePage={(event: unknown, newPage: number) => {
setPage(newPage);
}}
onChangeRowsPerPage={(
event: React.ChangeEvent<HTMLInputElement>
) => {
const rPP = parseInt(event.target.value, 10);
setRowsPerPage(rPP);
}}
ActionsComponent={MinTablePaginationActions}
/>
</TableRow>
</TableFooter>
</Table>
) : (
<div>No Notification Endpoints</div>
)}
</Paper>
</Grid> </Grid>
</Grid> </Grid>
</React.Fragment> </React.Fragment>

View File

@@ -26,6 +26,11 @@ export interface NotificationEndpointItem {
status: string; status: string;
} }
export interface TransformedEndpointItem {
service_name: string;
status: string;
}
export interface NotificationEndpointsList { export interface NotificationEndpointsList {
notification_endpoints: NotificationEndpointItem[]; notification_endpoints: NotificationEndpointItem[];
} }

View File

@@ -0,0 +1,28 @@
// This file is part of MinIO Console Server
// Copyright (c) 2020 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import { NotificationEndpointItem } from "./types";
export const notificationTransform = (
notificationElements: NotificationEndpointItem[]
) => {
return notificationElements.map(element => {
return {
service_name: `${element.service}:${element.account_id}`,
status: element.status
};
});
};

View File

@@ -16,21 +16,8 @@
import React from "react"; import React from "react";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles"; import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import Paper from "@material-ui/core/Paper";
import Grid from "@material-ui/core/Grid"; import Grid from "@material-ui/core/Grid";
import { import { Button } from "@material-ui/core";
Button,
IconButton,
LinearProgress,
TableFooter,
TablePagination
} from "@material-ui/core";
import DeleteIcon from "@material-ui/icons/Delete";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
import InputAdornment from "@material-ui/core/InputAdornment"; import InputAdornment from "@material-ui/core/InputAdornment";
@@ -41,7 +28,7 @@ import DeletePolicy from "./DeletePolicy";
import api from "../../../common/api"; import api from "../../../common/api";
import { CreateIcon } from "../../../icons"; import { CreateIcon } from "../../../icons";
import { MinTablePaginationActions } from "../../../common/MinTablePaginationActions"; import { MinTablePaginationActions } from "../../../common/MinTablePaginationActions";
import VisibilityIcon from "@material-ui/icons/Visibility"; import TableWrapper from "../Common/TableWrapper/TableWrapper";
const styles = (theme: Theme) => const styles = (theme: Theme) =>
createStyles({ createStyles({
@@ -206,6 +193,32 @@ class Policies extends React.Component<IPoliciesProps, IPoliciesState> {
this.setState({ deleteOpen: true, selectedPolicy: policy }); this.setState({ deleteOpen: true, selectedPolicy: policy });
}; };
const viewAction = (row: any) => {
this.setState({
addScreenOpen: true,
policyEdit: row
});
};
const tableActions = [
{ type: "view", onClick: viewAction },
{ type: "delete", onClick: confirmDeletePolicy, sendOnlyId: true }
];
const filteredRecords = records
.slice(offset, offset + rowsPerPage)
.filter((b: Policy) => {
if (filterPolicies === "") {
return true;
} else {
if (b.name.indexOf(filterPolicies) >= 0) {
return true;
} else {
return false;
}
}
});
return ( return (
<React.Fragment> <React.Fragment>
{addScreenOpen && ( {addScreenOpen && (
@@ -271,80 +284,28 @@ class Policies extends React.Component<IPoliciesProps, IPoliciesState> {
<br /> <br />
</Grid> </Grid>
<Grid item xs={12}> <Grid item xs={12}>
<Paper className={classes.paper}> <TableWrapper
{loading && <LinearProgress />} itemActions={tableActions}
{records != null && records.length > 0 ? ( columns={[{ label: "Name", elementKey: "name" }]}
<Table size="medium"> isLoading={loading}
<TableHead className={classes.minTableHeader}> records={filteredRecords}
<TableRow> entityName="Policies"
<TableCell>Name</TableCell> idField="name"
<TableCell align="right">Actions</TableCell> paginatorConfig={{
</TableRow> rowsPerPageOptions: [5, 10, 25],
</TableHead> colSpan: 3,
<TableBody> count: totalRecords,
{records rowsPerPage: rowsPerPage,
.slice(offset, offset + rowsPerPage) page: page,
.filter((b: Policy) => { SelectProps: {
if (filterPolicies === "") { inputProps: { "aria-label": "rows per page" },
return true; native: true
} else { },
if (b.name.indexOf(filterPolicies) >= 0) { onChangePage: handleChangePage,
return true; onChangeRowsPerPage: handleChangeRowsPerPage,
} else { ActionsComponent: MinTablePaginationActions
return false; }}
} />
}
})
.map(row => (
<TableRow key={row.name}>
<TableCell>{row.name}</TableCell>
<TableCell align="right">
<IconButton
aria-label="view"
onClick={() => {
this.setState({
addScreenOpen: true,
policyEdit: row
});
}}
>
<VisibilityIcon />
</IconButton>
<IconButton
aria-label="delete"
onClick={() => {
confirmDeletePolicy(row.name);
}}
>
<DeleteIcon />
</IconButton>
</TableCell>
</TableRow>
))}
</TableBody>
<TableFooter>
<TableRow>
<TablePagination
rowsPerPageOptions={[5, 10, 25]}
colSpan={3}
count={totalRecords}
rowsPerPage={rowsPerPage}
page={page}
SelectProps={{
inputProps: { "aria-label": "rows per page" },
native: true
}}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
ActionsComponent={MinTablePaginationActions}
/>
</TableRow>
</TableFooter>
</Table>
) : (
<div>No Policies</div>
)}
</Paper>
</Grid> </Grid>
</Grid> </Grid>
</React.Fragment> </React.Fragment>

View File

@@ -164,7 +164,7 @@ class AddUserContent extends React.Component<
addLoading: false, addLoading: false,
addError: "", addError: "",
accessKey: res.accessKey, accessKey: res.accessKey,
selectedGroups: res.memberOf, selectedGroups: res.memberOf || [],
enabled: res.status enabled: res.status
}); });
}) })

View File

@@ -17,23 +17,17 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles"; import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { LinearProgress } from "@material-ui/core"; import { LinearProgress } from "@material-ui/core";
import TableContainer from "@material-ui/core/TableContainer";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import Paper from "@material-ui/core/Paper"; import Paper from "@material-ui/core/Paper";
import Grid from "@material-ui/core/Grid"; import Grid from "@material-ui/core/Grid";
import Title from "../../../common/Title"; import Title from "../../../common/Title";
import InputAdornment from "@material-ui/core/InputAdornment"; import InputAdornment from "@material-ui/core/InputAdornment";
import SearchIcon from "@material-ui/icons/Search"; import SearchIcon from "@material-ui/icons/Search";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
import Checkbox from "@material-ui/core/Checkbox";
import api from "../../../common/api"; import api from "../../../common/api";
import { groupsSort } from "../../../utils/sortFunctions"; import { groupsSort } from "../../../utils/sortFunctions";
import { GroupsList } from "../Groups/types"; import { GroupsList } from "../Groups/types";
import get from "lodash/get"; import get from "lodash/get";
import TableWrapper from "../Common/TableWrapper/TableWrapper";
interface IGroupsProps { interface IGroupsProps {
classes: any; classes: any;
@@ -196,40 +190,17 @@ const GroupsSelectors = ({
}} }}
/> />
</Grid> </Grid>
<TableContainer className={classes.tableContainer}> <Grid item xs={12}>
<Table size="small" stickyHeader aria-label="sticky table"> <TableWrapper
<TableHead className={classes.minTableHeader}> columns={[{ label: "Group", elementKey: "" }]}
<TableRow> onSelect={selectionChanged}
<TableCell className={classes.stickyHeader}> selectedItems={selGroups}
Select isLoading={loading}
</TableCell> records={filteredRecords}
<TableCell className={classes.stickyHeader}> entityName="Groups"
Group idField=""
</TableCell> />
</TableRow> </Grid>
</TableHead>
<TableBody>
{filteredRecords.map(groupName => (
<TableRow key={`group-${groupName}`}>
<TableCell padding="checkbox">
<Checkbox
value={groupName}
color="primary"
inputProps={{
"aria-label": "secondary checkbox"
}}
onChange={selectionChanged}
checked={selGroups.includes(groupName)}
/>
</TableCell>
<TableCell className={classes.wrapCell}>
{groupName}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</React.Fragment> </React.Fragment>
) : ( ) : (
<div className={classes.noFound}>No Groups Available</div> <div className={classes.noFound}>No Groups Available</div>

View File

@@ -128,19 +128,15 @@ class Users extends React.Component<IUsersProps, IUsersState> {
api api
.invoke("GET", `/api/v1/users?offset=${offset}&limit=${rowsPerPage}`) .invoke("GET", `/api/v1/users?offset=${offset}&limit=${rowsPerPage}`)
.then((res: UsersList) => { .then((res: UsersList) => {
const usersList = !res.users ? [] : res.users;
this.setState({ this.setState({
loading: false, loading: false,
records: res.users.sort(usersSort), records: usersList.sort(usersSort),
totalRecords: res.users.length, totalRecords: res.users.length,
error: "" error: ""
}); });
// if we get 0 results, and page > 0 , go down 1 page // if we get 0 results, and page > 0 , go down 1 page
if ( if ((!usersList || res.users.length === 0) && page > 0) {
(res.users === undefined ||
res.users == null ||
res.users.length === 0) &&
page > 0
) {
const newPage = page - 1; const newPage = page - 1;
this.setState({ page: newPage }, () => { this.setState({ page: newPage }, () => {
this.fetchRecords(); this.fetchRecords();