385 lines
11 KiB
TypeScript
385 lines
11 KiB
TypeScript
// 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 React from "react";
|
|
import get from "lodash/get";
|
|
import isString from "lodash/isString";
|
|
import {
|
|
LinearProgress,
|
|
TablePagination,
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableRow,
|
|
Paper,
|
|
Grid,
|
|
Checkbox,
|
|
Typography,
|
|
} from "@material-ui/core";
|
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
|
import { TablePaginationActionsProps } from "@material-ui/core/TablePagination/TablePaginationActions";
|
|
import TableActionButton from "./TableActionButton";
|
|
import history from "../../../../history";
|
|
import { checkboxIcons } from "../FormComponents/common/styleLibrary";
|
|
|
|
//Interfaces for table Items
|
|
|
|
interface ItemActions {
|
|
type: string;
|
|
onClick?(valueToSend: any): any;
|
|
to?: string;
|
|
sendOnlyId?: boolean;
|
|
}
|
|
|
|
interface IColumns {
|
|
label: string;
|
|
elementKey: string;
|
|
sortable?: boolean;
|
|
renderFunction?: (input: any) => any;
|
|
globalClass?: any;
|
|
}
|
|
|
|
interface IPaginatorConfig {
|
|
rowsPerPageOptions: number[];
|
|
colSpan: number;
|
|
count: number;
|
|
rowsPerPage: number;
|
|
page: number;
|
|
SelectProps: any;
|
|
onChangePage: (
|
|
event: React.MouseEvent<HTMLButtonElement> | null,
|
|
page: number
|
|
) => void;
|
|
onChangeRowsPerPage?: React.ChangeEventHandler<
|
|
HTMLTextAreaElement | HTMLInputElement
|
|
>;
|
|
ActionsComponent?: React.ElementType<TablePaginationActionsProps>;
|
|
}
|
|
|
|
interface TableWrapperProps {
|
|
itemActions?: ItemActions[] | null;
|
|
columns: IColumns[];
|
|
onSelect?: (e: React.ChangeEvent<HTMLInputElement>) => any;
|
|
idField: string;
|
|
isLoading: boolean;
|
|
records: any[];
|
|
classes: any;
|
|
entityName: string;
|
|
selectedItems?: string[];
|
|
stickyHeader?: boolean;
|
|
paginatorConfig?: IPaginatorConfig;
|
|
}
|
|
|
|
const borderColor = "#eaeaea";
|
|
|
|
const rowText = {
|
|
fontWeight: 400,
|
|
fontSize: 14,
|
|
borderColor: borderColor,
|
|
};
|
|
|
|
const styles = (theme: Theme) =>
|
|
createStyles({
|
|
dialogContainer: {
|
|
padding: "12px 26px 22px",
|
|
},
|
|
paper: {
|
|
display: "flex",
|
|
overflow: "auto",
|
|
flexDirection: "column",
|
|
padding: "19px 38px",
|
|
minHeight: "200px",
|
|
},
|
|
minTableHeader: {
|
|
color: "#393939",
|
|
"& tr": {
|
|
"& th": {
|
|
fontWeight: 700,
|
|
fontSize: 14,
|
|
paddingBottom: 15,
|
|
borderColor: borderColor,
|
|
},
|
|
},
|
|
},
|
|
rowUnselected: {
|
|
...rowText,
|
|
},
|
|
rowSelected: {
|
|
...rowText,
|
|
color: "#201763",
|
|
},
|
|
paginatorContainer: {
|
|
display: "flex",
|
|
justifyContent: "flex-end",
|
|
padding: "5px 38px",
|
|
},
|
|
checkBoxHeader: {
|
|
"&.MuiTableCell-paddingCheckbox": {
|
|
paddingBottom: 9,
|
|
},
|
|
},
|
|
actionsContainer: {
|
|
width: 150,
|
|
borderColor: borderColor,
|
|
},
|
|
paginatorComponent: {
|
|
borderBottom: 0,
|
|
},
|
|
checkBoxRow: {
|
|
borderColor: borderColor,
|
|
},
|
|
loadingBox: {
|
|
paddingTop: "100px",
|
|
paddingBottom: "100px",
|
|
},
|
|
rowElement: {
|
|
userSelect: "none",
|
|
|
|
"&:hover": {
|
|
backgroundColor: "#ececec",
|
|
},
|
|
},
|
|
rowClickable: {
|
|
cursor: "pointer",
|
|
},
|
|
...checkboxIcons,
|
|
});
|
|
|
|
// Function that renders Title Columns
|
|
const titleColumnsMap = (columns: IColumns[]) => {
|
|
return columns.map((column: IColumns, index: number) => {
|
|
return (
|
|
<TableCell
|
|
key={`tbCT-${column.elementKey}-${index}`}
|
|
className={column.globalClass}
|
|
>
|
|
{column.label}
|
|
</TableCell>
|
|
);
|
|
});
|
|
};
|
|
|
|
// Function that renders Rows
|
|
const rowColumnsMap = (
|
|
columns: IColumns[],
|
|
itemData: any,
|
|
classes: any,
|
|
isSelected: boolean
|
|
) => {
|
|
return columns.map((column: IColumns, index: number) => {
|
|
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 (
|
|
<TableCell
|
|
key={`tbRE-${column.elementKey}-${index}`}
|
|
className={isSelected ? classes.rowSelected : classes.rowUnselected}
|
|
>
|
|
{renderElement}
|
|
</TableCell>
|
|
);
|
|
});
|
|
};
|
|
|
|
// Function to render the action buttons
|
|
const elementActions = (
|
|
actions: ItemActions[],
|
|
valueToSend: any,
|
|
selected: boolean,
|
|
idField: string
|
|
) => {
|
|
return actions.map((action: ItemActions, index: number) => {
|
|
if (action.type === "view") {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<TableActionButton
|
|
type={action.type}
|
|
onClick={action.onClick}
|
|
to={action.to}
|
|
valueToSend={valueToSend}
|
|
selected={selected}
|
|
key={`actions-${action.type}-${index.toString()}`}
|
|
idField={idField}
|
|
sendOnlyId={!!action.sendOnlyId}
|
|
/>
|
|
);
|
|
});
|
|
};
|
|
|
|
// Main function to render the Table Wrapper
|
|
const TableWrapper = ({
|
|
itemActions,
|
|
columns,
|
|
onSelect,
|
|
records,
|
|
isLoading,
|
|
entityName,
|
|
selectedItems,
|
|
idField,
|
|
classes,
|
|
stickyHeader = false,
|
|
paginatorConfig,
|
|
}: TableWrapperProps) => {
|
|
const findView = itemActions
|
|
? itemActions.find((el) => el.type === "view")
|
|
: null;
|
|
|
|
const clickAction = (rowItem: any) => {
|
|
if (findView) {
|
|
const valueClick = findView.sendOnlyId ? rowItem[idField] : rowItem;
|
|
if (findView.to) {
|
|
history.push(`${findView.to}/${valueClick}`);
|
|
return;
|
|
}
|
|
|
|
if (findView.onClick) {
|
|
findView.onClick(valueClick);
|
|
}
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Grid item xs={12}>
|
|
<Paper className={classes.paper}>
|
|
{isLoading && (
|
|
<Grid container className={classes.loadingBox}>
|
|
<Grid item xs={12} style={{ textAlign: "center" }}>
|
|
<Typography component="h3">Loading...</Typography>
|
|
</Grid>
|
|
<Grid item xs={12}>
|
|
<LinearProgress />
|
|
</Grid>
|
|
</Grid>
|
|
)}
|
|
{records && !isLoading && records.length > 0 ? (
|
|
<Table size="small" stickyHeader={stickyHeader}>
|
|
<TableHead className={classes.minTableHeader}>
|
|
<TableRow>
|
|
{onSelect && selectedItems && (
|
|
<TableCell
|
|
padding="checkbox"
|
|
align="center"
|
|
className={classes.checkBoxHeader}
|
|
>
|
|
Select
|
|
</TableCell>
|
|
)}
|
|
{titleColumnsMap(columns)}
|
|
{((itemActions && itemActions.length > 1) ||
|
|
(itemActions &&
|
|
itemActions.length === 1 &&
|
|
itemActions[0].type !== "view")) && (
|
|
<TableCell
|
|
align="center"
|
|
className={classes.actionsContainer}
|
|
>
|
|
Actions
|
|
</TableCell>
|
|
)}
|
|
</TableRow>
|
|
</TableHead>
|
|
<TableBody>
|
|
{records.map((record: any, index: number) => {
|
|
const isSelected = selectedItems
|
|
? selectedItems.includes(
|
|
isString(record) ? record : record[idField]
|
|
)
|
|
: false;
|
|
|
|
return (
|
|
<TableRow
|
|
key={`tb-${entityName}-${index.toString()}`}
|
|
className={`${findView ? classes.rowClickable : ""} ${
|
|
classes.rowElement
|
|
}`}
|
|
onClick={() => {
|
|
clickAction(record);
|
|
}}
|
|
>
|
|
{onSelect && selectedItems && (
|
|
<TableCell
|
|
padding="checkbox"
|
|
align="center"
|
|
className={classes.checkBoxRow}
|
|
>
|
|
<Checkbox
|
|
value={isString(record) ? record : record[idField]}
|
|
color="primary"
|
|
inputProps={{ "aria-label": "secondary checkbox" }}
|
|
checked={isSelected}
|
|
onChange={onSelect}
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
}}
|
|
checkedIcon={<span className={classes.checkedIcon} />}
|
|
icon={<span className={classes.unCheckedIcon} />}
|
|
/>
|
|
</TableCell>
|
|
)}
|
|
{rowColumnsMap(columns, record, classes, isSelected)}
|
|
{((itemActions && itemActions.length > 1) ||
|
|
(itemActions &&
|
|
itemActions.length === 1 &&
|
|
itemActions[0].type !== "view")) && (
|
|
<TableCell
|
|
align="center"
|
|
className={classes.actionsContainer}
|
|
>
|
|
{elementActions(
|
|
itemActions,
|
|
record,
|
|
isSelected,
|
|
idField
|
|
)}
|
|
</TableCell>
|
|
)}
|
|
</TableRow>
|
|
);
|
|
})}
|
|
</TableBody>
|
|
</Table>
|
|
) : (
|
|
<React.Fragment>
|
|
{!isLoading && <div>{`There are no ${entityName} yet.`}</div>}
|
|
</React.Fragment>
|
|
)}
|
|
</Paper>
|
|
{paginatorConfig && (
|
|
<Grid item xs={12} className={classes.paginatorContainer}>
|
|
<Table>
|
|
<TableBody>
|
|
<TableRow>
|
|
<TablePagination
|
|
{...paginatorConfig}
|
|
className={classes.paginatorComponent}
|
|
/>
|
|
</TableRow>
|
|
</TableBody>
|
|
</Table>
|
|
</Grid>
|
|
)}
|
|
</Grid>
|
|
);
|
|
};
|
|
|
|
export default withStyles(styles)(TableWrapper);
|