Added loading validation to avoid flashing empty components (#1177)
Signed-off-by: Benjamin Perez <benjamin@bexsoft.net> Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
@@ -168,31 +168,33 @@ const BucketEventsPanel = ({
|
||||
customPaperHeight={classes.twHeight}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<HelpBox
|
||||
title={"Lambda Notifications"}
|
||||
iconComponent={<LambdaIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
MinIO bucket notifications allow administrators to send
|
||||
notifications to supported external services on certain object
|
||||
or bucket events. MinIO supports bucket and object-level S3
|
||||
events similar to the Amazon S3 Event Notifications.
|
||||
<br />
|
||||
<br />
|
||||
You can learn more at our{" "}
|
||||
<a
|
||||
href="https://docs.min.io/minio/baremetal/monitoring/bucket-notifications/bucket-notifications.html?ref=con"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
documentation
|
||||
</a>
|
||||
.
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
{!loadingEvents && (
|
||||
<Grid item xs={12}>
|
||||
<HelpBox
|
||||
title={"Lambda Notifications"}
|
||||
iconComponent={<LambdaIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
MinIO bucket notifications allow administrators to send
|
||||
notifications to supported external services on certain object
|
||||
or bucket events. MinIO supports bucket and object-level S3
|
||||
events similar to the Amazon S3 Event Notifications.
|
||||
<br />
|
||||
<br />
|
||||
You can learn more at our{" "}
|
||||
<a
|
||||
href="https://docs.min.io/minio/baremetal/monitoring/bucket-notifications/bucket-notifications.html?ref=con"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
documentation
|
||||
</a>
|
||||
.
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
@@ -208,31 +208,33 @@ const BucketLifecyclePanel = ({
|
||||
customPaperHeight={classes.twHeight}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<HelpBox
|
||||
title={"Lifecycle Rules"}
|
||||
iconComponent={<TiersIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
MinIO Object Lifecycle Management allows creating rules for time
|
||||
or date based automatic transition or expiry of objects. For
|
||||
object transition, MinIO automatically moves the object to a
|
||||
configured remote storage tier.
|
||||
<br />
|
||||
<br />
|
||||
You can learn more at our{" "}
|
||||
<a
|
||||
href="https://docs.min.io/minio/baremetal/lifecycle-management/lifecycle-management-overview.html?ref=con"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
documentation
|
||||
</a>
|
||||
.
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
{!loadingLifecycle && (
|
||||
<Grid item xs={12}>
|
||||
<HelpBox
|
||||
title={"Lifecycle Rules"}
|
||||
iconComponent={<TiersIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
MinIO Object Lifecycle Management allows creating rules for
|
||||
time or date based automatic transition or expiry of objects.
|
||||
For object transition, MinIO automatically moves the object to
|
||||
a configured remote storage tier.
|
||||
<br />
|
||||
<br />
|
||||
You can learn more at our{" "}
|
||||
<a
|
||||
href="https://docs.min.io/minio/baremetal/lifecycle-management/lifecycle-management-overview.html?ref=con"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
documentation
|
||||
</a>
|
||||
.
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
@@ -19,7 +19,7 @@ import { connect } from "react-redux";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
import { Box, Button } from "@mui/material";
|
||||
import { Box, Button, LinearProgress } from "@mui/material";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import InputAdornment from "@mui/material/InputAdornment";
|
||||
@@ -28,13 +28,12 @@ import { Bucket, BucketList, HasPermissionResponse } from "../types";
|
||||
import {
|
||||
AddIcon,
|
||||
BucketsIcon,
|
||||
TenantsIcon,
|
||||
WatchIcon,
|
||||
} from "../../../../icons";
|
||||
import { AppState } from "../../../../store";
|
||||
import { addBucketOpen, addBucketReset } from "../actions";
|
||||
import { setErrorSnackMessage } from "../../../../actions";
|
||||
import { containerForHeader } from "../../Common/FormComponents/common/styleLibrary";
|
||||
import { containerForHeader, linkStyles } from "../../Common/FormComponents/common/styleLibrary";
|
||||
import { ErrorResponseHandler } from "../../../../common/types";
|
||||
import api from "../../../../common/api";
|
||||
import AddBucket from "./AddBucket";
|
||||
@@ -127,10 +126,7 @@ const styles = (theme: Theme) =>
|
||||
constrainedContainer: {
|
||||
maxWidth: 1180,
|
||||
},
|
||||
link: {
|
||||
textDecoration: "underline",
|
||||
color: theme.palette.info.main,
|
||||
},
|
||||
...linkStyles(theme.palette.info.main),
|
||||
});
|
||||
|
||||
interface IListBucketsProps {
|
||||
@@ -408,53 +404,57 @@ const ListBuckets = ({
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
{filteredRecords.map((bucket, index) => {
|
||||
return (
|
||||
<BucketListItem
|
||||
bucket={bucket}
|
||||
key={`bucketListItem-${index.toString()}`}
|
||||
onDelete={confirmDeleteBucket}
|
||||
onSelect={selectListBuckets}
|
||||
selected={selectedBuckets.includes(bucket.name)}
|
||||
bulkSelect={bulkSelect}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{filteredRecords.length == 0 && (
|
||||
<Grid
|
||||
container
|
||||
justifyContent={"center"}
|
||||
alignContent={"center"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Grid item xs={8}>
|
||||
<HelpBox
|
||||
iconComponent={<BucketsIcon />}
|
||||
title={"Buckets"}
|
||||
help={
|
||||
<Fragment>
|
||||
MinIO uses buckets to organize objects. A bucket is
|
||||
similar to a folder or directory in a filesystem, where
|
||||
each bucket can hold an arbitrary number of objects.
|
||||
<br />
|
||||
<br />
|
||||
To get started,
|
||||
<a
|
||||
className={classes.link}
|
||||
onClick={() => {
|
||||
addBucketOpen(true);
|
||||
}}
|
||||
>
|
||||
Create a Bucket.
|
||||
</a>
|
||||
</Fragment>
|
||||
}
|
||||
{loading && <LinearProgress />}
|
||||
{!loading && (
|
||||
<Grid item xs={12}>
|
||||
{filteredRecords.map((bucket, index) => {
|
||||
return (
|
||||
<BucketListItem
|
||||
bucket={bucket}
|
||||
key={`bucketListItem-${index.toString()}`}
|
||||
onDelete={confirmDeleteBucket}
|
||||
onSelect={selectListBuckets}
|
||||
selected={selectedBuckets.includes(bucket.name)}
|
||||
bulkSelect={bulkSelect}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{filteredRecords.length === 0 && (
|
||||
<Grid
|
||||
container
|
||||
justifyContent={"center"}
|
||||
alignContent={"center"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Grid item xs={8}>
|
||||
<HelpBox
|
||||
iconComponent={<BucketsIcon />}
|
||||
title={"Buckets"}
|
||||
help={
|
||||
<Fragment>
|
||||
MinIO uses buckets to organize objects. A bucket is
|
||||
similar to a folder or directory in a filesystem,
|
||||
where each bucket can hold an arbitrary number of
|
||||
objects.
|
||||
<br />
|
||||
<br />
|
||||
To get started,
|
||||
<button
|
||||
className={classes.link}
|
||||
onClick={() => {
|
||||
addBucketOpen(true);
|
||||
}}
|
||||
>
|
||||
Create a Bucket.
|
||||
</button>
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
|
||||
@@ -965,3 +965,13 @@ export const commonDashboardInfocard = {
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const linkStyles = (color: string) => ({
|
||||
link: {
|
||||
textDecoration: "underline",
|
||||
color,
|
||||
backgroundColor: "transparent",
|
||||
border: 0,
|
||||
cursor: "pointer",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -20,13 +20,14 @@ import { connect } from "react-redux";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
import { IconButton, TextField } from "@mui/material";
|
||||
import { IconButton, LinearProgress, TextField } from "@mui/material";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import Button from "@mui/material/Button";
|
||||
import InputAdornment from "@mui/material/InputAdornment";
|
||||
import {
|
||||
actionsTray,
|
||||
containerForHeader,
|
||||
linkStyles,
|
||||
searchField,
|
||||
settingsCommon,
|
||||
typesSelection,
|
||||
@@ -76,10 +77,7 @@ const styles = (theme: Theme) =>
|
||||
...settingsCommon.customTitle,
|
||||
marginTop: 0,
|
||||
},
|
||||
link: {
|
||||
textDecoration: "underline",
|
||||
color: theme.palette.info.main,
|
||||
},
|
||||
...linkStyles(theme.palette.info.main),
|
||||
});
|
||||
|
||||
const ListTiersConfiguration = ({
|
||||
@@ -235,122 +233,127 @@ const ListTiersConfiguration = ({
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
{records.length > 0 && (
|
||||
{isLoading && <LinearProgress />}
|
||||
{!isLoading && (
|
||||
<Fragment>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={[
|
||||
{
|
||||
type: "edit",
|
||||
onClick: (tierData: ITierElement) => {
|
||||
setSelectedTier(tierData);
|
||||
setUpdateCredentialsOpen(true);
|
||||
},
|
||||
},
|
||||
]}
|
||||
columns={[
|
||||
{
|
||||
label: "Tier Name",
|
||||
elementKey: "type",
|
||||
renderFunction: renderTierName,
|
||||
renderFullObject: true,
|
||||
},
|
||||
{
|
||||
label: "Type",
|
||||
elementKey: "type",
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
label: "Endpoint",
|
||||
elementKey: "type",
|
||||
renderFunction: renderTierEndpoint,
|
||||
renderFullObject: true,
|
||||
},
|
||||
{
|
||||
label: "Bucket",
|
||||
elementKey: "type",
|
||||
renderFunction: renderTierBucket,
|
||||
renderFullObject: true,
|
||||
},
|
||||
{
|
||||
label: "Prefix",
|
||||
elementKey: "type",
|
||||
renderFunction: renderTierPrefix,
|
||||
renderFullObject: true,
|
||||
},
|
||||
{
|
||||
label: "Region",
|
||||
elementKey: "type",
|
||||
renderFunction: renderTierRegion,
|
||||
renderFullObject: true,
|
||||
},
|
||||
]}
|
||||
isLoading={isLoading}
|
||||
records={filteredRecords}
|
||||
entityName="Tiers"
|
||||
idField="service_name"
|
||||
customPaperHeight={classes.customConfigurationPage}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<HelpBox
|
||||
title={"Learn more about TIERS"}
|
||||
iconComponent={<TiersIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
Tiers are used by the MinIO Object Lifecycle Management
|
||||
which allows creating rules for time or date based automatic
|
||||
transition or expiry of objects. For object transition,
|
||||
MinIO automatically moves the object to a configured remote
|
||||
storage tier.
|
||||
<br />
|
||||
<br />
|
||||
You can learn more at our{" "}
|
||||
<a
|
||||
href="https://docs.min.io/minio/baremetal/lifecycle-management/lifecycle-management-overview.html?ref=con"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
documentation
|
||||
</a>
|
||||
.
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
{records.length > 0 && (
|
||||
<Fragment>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={[
|
||||
{
|
||||
type: "edit",
|
||||
onClick: (tierData: ITierElement) => {
|
||||
setSelectedTier(tierData);
|
||||
setUpdateCredentialsOpen(true);
|
||||
},
|
||||
},
|
||||
]}
|
||||
columns={[
|
||||
{
|
||||
label: "Tier Name",
|
||||
elementKey: "type",
|
||||
renderFunction: renderTierName,
|
||||
renderFullObject: true,
|
||||
},
|
||||
{
|
||||
label: "Type",
|
||||
elementKey: "type",
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
label: "Endpoint",
|
||||
elementKey: "type",
|
||||
renderFunction: renderTierEndpoint,
|
||||
renderFullObject: true,
|
||||
},
|
||||
{
|
||||
label: "Bucket",
|
||||
elementKey: "type",
|
||||
renderFunction: renderTierBucket,
|
||||
renderFullObject: true,
|
||||
},
|
||||
{
|
||||
label: "Prefix",
|
||||
elementKey: "type",
|
||||
renderFunction: renderTierPrefix,
|
||||
renderFullObject: true,
|
||||
},
|
||||
{
|
||||
label: "Region",
|
||||
elementKey: "type",
|
||||
renderFunction: renderTierRegion,
|
||||
renderFullObject: true,
|
||||
},
|
||||
]}
|
||||
isLoading={isLoading}
|
||||
records={filteredRecords}
|
||||
entityName="Tiers"
|
||||
idField="service_name"
|
||||
customPaperHeight={classes.customConfigurationPage}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<HelpBox
|
||||
title={"Learn more about TIERS"}
|
||||
iconComponent={<TiersIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
Tiers are used by the MinIO Object Lifecycle Management
|
||||
which allows creating rules for time or date based
|
||||
automatic transition or expiry of objects. For object
|
||||
transition, MinIO automatically moves the object to a
|
||||
configured remote storage tier.
|
||||
<br />
|
||||
<br />
|
||||
You can learn more at our{" "}
|
||||
<a
|
||||
href="https://docs.min.io/minio/baremetal/lifecycle-management/lifecycle-management-overview.html?ref=con"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
documentation
|
||||
</a>
|
||||
.
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)}
|
||||
{records.length === 0 && (
|
||||
<Grid
|
||||
container
|
||||
justifyContent={"center"}
|
||||
alignContent={"center"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Grid item xs={8}>
|
||||
<HelpBox
|
||||
title={"Tiers"}
|
||||
iconComponent={<TiersIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
Tiers are used by the MinIO Object Lifecycle Management
|
||||
which allows creating rules for time or date based
|
||||
automatic transition or expiry of objects. For object
|
||||
transition, MinIO automatically moves the object to a
|
||||
configured remote storage tier.
|
||||
<br />
|
||||
<br />
|
||||
To get started,{" "}
|
||||
<button onClick={addTier} className={classes.link}>
|
||||
Add Tier
|
||||
</button>
|
||||
.
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
{records.length == 0 && (
|
||||
<Grid
|
||||
container
|
||||
justifyContent={"center"}
|
||||
alignContent={"center"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Grid item xs={8}>
|
||||
<HelpBox
|
||||
title={"Tiers"}
|
||||
iconComponent={<TiersIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
Tiers are used by the MinIO Object Lifecycle Management
|
||||
which allows creating rules for time or date based automatic
|
||||
transition or expiry of objects. For object transition,
|
||||
MinIO automatically moves the object to a configured remote
|
||||
storage tier.
|
||||
<br />
|
||||
<br />
|
||||
To get started,{" "}
|
||||
<a onClick={addTier} className={classes.link}>
|
||||
Add Tier
|
||||
</a>
|
||||
.
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
@@ -22,7 +22,7 @@ import withStyles from "@mui/styles/withStyles";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import InputAdornment from "@mui/material/InputAdornment";
|
||||
import { Button } from "@mui/material";
|
||||
import { Button, LinearProgress } from "@mui/material";
|
||||
import { AddIcon, GroupsIcon, UsersIcon } from "../../../icons";
|
||||
import { setErrorSnackMessage } from "../../../actions";
|
||||
import { GroupsList } from "./types";
|
||||
@@ -30,6 +30,7 @@ import { stringSort } from "../../../utils/sortFunctions";
|
||||
import {
|
||||
actionsTray,
|
||||
containerForHeader,
|
||||
linkStyles,
|
||||
searchField,
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
import { ErrorResponseHandler } from "../../../common/types";
|
||||
@@ -80,10 +81,7 @@ const styles = (theme: Theme) =>
|
||||
},
|
||||
},
|
||||
},
|
||||
link: {
|
||||
textDecoration: "underline",
|
||||
color: theme.palette.info.main,
|
||||
},
|
||||
...linkStyles(theme.palette.info.main),
|
||||
...actionsTray,
|
||||
...searchField,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
@@ -230,81 +228,86 @@ const Groups = ({ classes, setErrorSnackMessage }: IGroupsProps) => {
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
{records.length > 0 && (
|
||||
{loading && <LinearProgress />}
|
||||
{!loading && (
|
||||
<Fragment>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[{ label: "Name", elementKey: "" }]}
|
||||
isLoading={loading}
|
||||
records={filteredRecords}
|
||||
entityName="Groups"
|
||||
idField=""
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<HelpBox
|
||||
title={"Groups"}
|
||||
iconComponent={<GroupsIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
A group can have one attached IAM policy, where all users
|
||||
with membership in that group inherit that policy. Groups
|
||||
support more simplified management of user permissions on
|
||||
the MinIO Tenant.
|
||||
<br />
|
||||
<br />
|
||||
You can learn more at our{" "}
|
||||
<a
|
||||
href="https://docs.min.io/minio/k8s/tutorials/group-management.html?ref=con"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
documentation
|
||||
</a>
|
||||
.
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
{records.length > 0 && (
|
||||
<Fragment>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[{ label: "Name", elementKey: "" }]}
|
||||
isLoading={loading}
|
||||
records={filteredRecords}
|
||||
entityName="Groups"
|
||||
idField=""
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<HelpBox
|
||||
title={"Groups"}
|
||||
iconComponent={<GroupsIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
A group can have one attached IAM policy, where all
|
||||
users with membership in that group inherit that
|
||||
policy. Groups support more simplified management of
|
||||
user permissions on the MinIO Tenant.
|
||||
<br />
|
||||
<br />
|
||||
You can learn more at our{" "}
|
||||
<a
|
||||
href="https://docs.min.io/minio/k8s/tutorials/group-management.html?ref=con"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
documentation
|
||||
</a>
|
||||
.
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)}
|
||||
{records.length === 0 && (
|
||||
<Grid
|
||||
container
|
||||
justifyContent={"center"}
|
||||
alignContent={"center"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Grid item xs={8}>
|
||||
<HelpBox
|
||||
title={"Groups"}
|
||||
iconComponent={<UsersIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
A group can have one attached IAM policy, where all
|
||||
users with membership in that group inherit that
|
||||
policy. Groups support more simplified management of
|
||||
user permissions on the MinIO Tenant.
|
||||
<br />
|
||||
<br />
|
||||
To get started,{" "}
|
||||
<button
|
||||
onClick={() => {
|
||||
setSelectedGroup(null);
|
||||
setGroupOpen(true);
|
||||
}}
|
||||
className={classes.link}
|
||||
>
|
||||
Create a Group
|
||||
</button>
|
||||
.
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
{records.length == 0 && (
|
||||
<Grid
|
||||
container
|
||||
justifyContent={"center"}
|
||||
alignContent={"center"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Grid item xs={8}>
|
||||
<HelpBox
|
||||
title={"Groups"}
|
||||
iconComponent={<UsersIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
A group can have one attached IAM policy, where all users
|
||||
with membership in that group inherit that policy. Groups
|
||||
support more simplified management of user permissions on
|
||||
the MinIO Tenant.
|
||||
<br />
|
||||
<br />
|
||||
To get started,{" "}
|
||||
<a
|
||||
onClick={() => {
|
||||
setSelectedGroup(null);
|
||||
setGroupOpen(true);
|
||||
}}
|
||||
className={classes.link}
|
||||
>
|
||||
Create a Group
|
||||
</a>
|
||||
.
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
|
||||
@@ -37,7 +37,6 @@ import { ErrorResponseHandler } from "../../../../common/types";
|
||||
import api from "../../../../common/api";
|
||||
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
|
||||
import FilterInputWrapper from "../../Common/FormComponents/FilterInputWrapper/FilterInputWrapper";
|
||||
import DateTimePickerWrapper from "../../Common/FormComponents/DateTimePickerWrapper/DateTimePickerWrapper";
|
||||
import LogSearchFullModal from "./LogSearchFullModal";
|
||||
import { LogSearchColumnLabels } from "./utils";
|
||||
import DateRangeSelector from "../../Common/FormComponents/DateRangeSelector/DateRangeSelector";
|
||||
|
||||
@@ -19,7 +19,7 @@ import { connect } from "react-redux";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
import { IconButton, TextField } from "@mui/material";
|
||||
import { IconButton, LinearProgress, TextField } from "@mui/material";
|
||||
import { red } from "@mui/material/colors";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import FiberManualRecordIcon from "@mui/icons-material/FiberManualRecord";
|
||||
@@ -37,6 +37,7 @@ import { setErrorSnackMessage } from "../../../actions";
|
||||
import {
|
||||
actionsTray,
|
||||
containerForHeader,
|
||||
linkStyles,
|
||||
searchField,
|
||||
settingsCommon,
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
@@ -73,10 +74,7 @@ const styles = (theme: Theme) =>
|
||||
lambdaContainer: {
|
||||
padding: "15px 0",
|
||||
},
|
||||
link: {
|
||||
textDecoration: "underline",
|
||||
color: theme.palette.info.main,
|
||||
},
|
||||
...linkStyles(theme.palette.info.main),
|
||||
});
|
||||
|
||||
const ListNotificationEndpoints = ({
|
||||
@@ -186,91 +184,96 @@ const ListNotificationEndpoints = ({
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
{records.length > 0 && (
|
||||
{isLoading && <LinearProgress />}
|
||||
{!isLoading && (
|
||||
<Fragment>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={[]}
|
||||
columns={[
|
||||
{
|
||||
label: "Status",
|
||||
elementKey: "status",
|
||||
renderFunction: statusDisplay,
|
||||
width: 150,
|
||||
},
|
||||
{ label: "Service", elementKey: "service_name" },
|
||||
]}
|
||||
isLoading={isLoading}
|
||||
records={filteredRecords}
|
||||
entityName="Notification Endpoints"
|
||||
idField="service_name"
|
||||
customPaperHeight={classes.twHeight}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<HelpBox
|
||||
title={"Notification Endpoints"}
|
||||
iconComponent={<LambdaIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
MinIO bucket notifications allow administrators to send
|
||||
notifications to supported external services on certain
|
||||
object or bucket events. MinIO supports bucket and
|
||||
object-level S3 events similar to the Amazon S3 Event
|
||||
Notifications.
|
||||
<br />
|
||||
<br />
|
||||
You can learn more at our{" "}
|
||||
<a
|
||||
href="https://docs.min.io/minio/baremetal/monitoring/bucket-notifications/bucket-notifications.html?ref=con"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
documentation
|
||||
</a>
|
||||
.
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
{records.length > 0 && (
|
||||
<Fragment>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={[]}
|
||||
columns={[
|
||||
{
|
||||
label: "Status",
|
||||
elementKey: "status",
|
||||
renderFunction: statusDisplay,
|
||||
width: 150,
|
||||
},
|
||||
{ label: "Service", elementKey: "service_name" },
|
||||
]}
|
||||
isLoading={isLoading}
|
||||
records={filteredRecords}
|
||||
entityName="Notification Endpoints"
|
||||
idField="service_name"
|
||||
customPaperHeight={classes.twHeight}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<HelpBox
|
||||
title={"Notification Endpoints"}
|
||||
iconComponent={<LambdaIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
MinIO bucket notifications allow administrators to send
|
||||
notifications to supported external services on certain
|
||||
object or bucket events. MinIO supports bucket and
|
||||
object-level S3 events similar to the Amazon S3 Event
|
||||
Notifications.
|
||||
<br />
|
||||
<br />
|
||||
You can learn more at our{" "}
|
||||
<a
|
||||
href="https://docs.min.io/minio/baremetal/monitoring/bucket-notifications/bucket-notifications.html?ref=con"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
documentation
|
||||
</a>
|
||||
.
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)}
|
||||
{records.length === 0 && (
|
||||
<Grid
|
||||
container
|
||||
justifyContent={"center"}
|
||||
alignContent={"center"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Grid item xs={8}>
|
||||
<HelpBox
|
||||
title={"Notification Targets"}
|
||||
iconComponent={<LambdaIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
MinIO bucket notifications allow administrators to send
|
||||
notifications to supported external services on certain
|
||||
object or bucket events. MinIO supports bucket and
|
||||
object-level S3 events similar to the Amazon S3 Event
|
||||
Notifications.
|
||||
<br />
|
||||
<br />
|
||||
To get started,{" "}
|
||||
<button
|
||||
onClick={() => {
|
||||
history.push("/notification-endpoints/add");
|
||||
}}
|
||||
className={classes.link}
|
||||
>
|
||||
Add a Notification Target
|
||||
</button>
|
||||
.
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
{records.length == 0 && (
|
||||
<Grid
|
||||
container
|
||||
justifyContent={"center"}
|
||||
alignContent={"center"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Grid item xs={8}>
|
||||
<HelpBox
|
||||
title={"Notification Targets"}
|
||||
iconComponent={<LambdaIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
MinIO bucket notifications allow administrators to send
|
||||
notifications to supported external services on certain
|
||||
object or bucket events. MinIO supports bucket and
|
||||
object-level S3 events similar to the Amazon S3 Event
|
||||
Notifications.
|
||||
<br />
|
||||
<br />
|
||||
To get started,{" "}
|
||||
<a
|
||||
onClick={() => {
|
||||
history.push("/notification-endpoints/add");
|
||||
}}
|
||||
className={classes.link}
|
||||
>
|
||||
Add a Notification Target
|
||||
</a>
|
||||
.
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
@@ -19,7 +19,7 @@ import { connect } from "react-redux";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import InputAdornment from "@mui/material/InputAdornment";
|
||||
import { Box, Button, IconButton } from "@mui/material";
|
||||
import { Box, Button, IconButton, LinearProgress } from "@mui/material";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
@@ -29,6 +29,7 @@ import { NewServiceAccount } from "../../Common/CredentialsPrompt/types";
|
||||
import {
|
||||
actionsTray,
|
||||
containerForHeader,
|
||||
linkStyles,
|
||||
searchField,
|
||||
} from "../../Common/FormComponents/common/styleLibrary";
|
||||
import { setErrorSnackMessage } from "../../../../actions";
|
||||
@@ -104,10 +105,7 @@ const styles = (theme: Theme) =>
|
||||
paddingTop: 30,
|
||||
paddingBottom: 30,
|
||||
},
|
||||
link: {
|
||||
textDecoration: "underline",
|
||||
color: theme.palette.info.main,
|
||||
},
|
||||
...linkStyles(theme.palette.info.main),
|
||||
});
|
||||
|
||||
const ListTenants = ({ classes, setErrorSnackMessage }: ITenantsList) => {
|
||||
@@ -273,43 +271,49 @@ const ListTenants = ({ classes, setErrorSnackMessage }: ITenantsList) => {
|
||||
<RefreshIcon />
|
||||
</IconButton>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
{filteredRecords.map((t) => {
|
||||
return <TenantListItem tenant={t} />;
|
||||
})}
|
||||
{filteredRecords.length == 0 && (
|
||||
<Grid
|
||||
container
|
||||
justifyContent={"center"}
|
||||
alignContent={"center"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Grid item xs={8}>
|
||||
<HelpBox
|
||||
iconComponent={<TenantsIcon />}
|
||||
title={"Tenants"}
|
||||
help={
|
||||
<Fragment>
|
||||
Tenant is the logical structure to represent a MinIO
|
||||
deployment. A tenant can have different size and
|
||||
configurations from other tenants, even a different
|
||||
storage class.
|
||||
<br />
|
||||
<br />
|
||||
To get started,
|
||||
<a
|
||||
className={classes.link}
|
||||
onClick={() => {
|
||||
history.push("/tenants/add");
|
||||
}}
|
||||
>
|
||||
Create a Tenant.
|
||||
</a>
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
{isLoading && <LinearProgress />}
|
||||
{!isLoading && (
|
||||
<Fragment>
|
||||
{filteredRecords.map((t) => {
|
||||
return <TenantListItem tenant={t} />;
|
||||
})}
|
||||
{filteredRecords.length === 0 && (
|
||||
<Grid
|
||||
container
|
||||
justifyContent={"center"}
|
||||
alignContent={"center"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Grid item xs={8}>
|
||||
<HelpBox
|
||||
iconComponent={<TenantsIcon />}
|
||||
title={"Tenants"}
|
||||
help={
|
||||
<Fragment>
|
||||
Tenant is the logical structure to represent a
|
||||
MinIO deployment. A tenant can have different size
|
||||
and configurations from other tenants, even a
|
||||
different storage class.
|
||||
<br />
|
||||
<br />
|
||||
To get started,
|
||||
<button
|
||||
className={classes.link}
|
||||
onClick={() => {
|
||||
history.push("/tenants/add");
|
||||
}}
|
||||
>
|
||||
Create a Tenant.
|
||||
</button>
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
@@ -14,13 +14,19 @@
|
||||
// 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, { Fragment, useCallback, useEffect, useState } from "react";
|
||||
import React, { Fragment, useEffect, useState } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
import api from "../../../common/api";
|
||||
import { Button, Grid, InputAdornment, TextField } from "@mui/material";
|
||||
import {
|
||||
Button,
|
||||
Grid,
|
||||
InputAdornment,
|
||||
LinearProgress,
|
||||
TextField,
|
||||
} from "@mui/material";
|
||||
import GroupIcon from "@mui/icons-material/Group";
|
||||
import { User, UsersList } from "./types";
|
||||
import { usersSort } from "../../../utils/sortFunctions";
|
||||
@@ -28,6 +34,7 @@ import { AddIcon, UsersIcon } from "../../../icons";
|
||||
import {
|
||||
actionsTray,
|
||||
containerForHeader,
|
||||
linkStyles,
|
||||
searchField,
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
import { setErrorSnackMessage } from "../../../actions";
|
||||
@@ -80,10 +87,7 @@ const styles = (theme: Theme) =>
|
||||
...actionsTray,
|
||||
...searchField,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
link: {
|
||||
textDecoration: "underline",
|
||||
color: theme.palette.info.main,
|
||||
},
|
||||
...linkStyles(theme.palette.info.main),
|
||||
});
|
||||
|
||||
interface IUsersProps {
|
||||
@@ -94,7 +98,7 @@ interface IUsersProps {
|
||||
|
||||
const ListUsers = ({ classes, setErrorSnackMessage, history }: IUsersProps) => {
|
||||
const [records, setRecords] = useState<User[]>([]);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [addScreenOpen, setAddScreenOpen] = useState<boolean>(false);
|
||||
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
||||
const [selectedUser, setSelectedUser] = useState<User | null>(null);
|
||||
@@ -103,31 +107,15 @@ const ListUsers = ({ classes, setErrorSnackMessage, history }: IUsersProps) => {
|
||||
const [checkedUsers, setCheckedUsers] = useState<string[]>([]);
|
||||
const [policyOpen, setPolicyOpen] = useState<boolean>(false);
|
||||
|
||||
const fetchRecords = useCallback(() => {
|
||||
setLoading(true);
|
||||
api
|
||||
.invoke("GET", `/api/v1/users`)
|
||||
.then((res: UsersList) => {
|
||||
const users = res.users === null ? [] : res.users;
|
||||
|
||||
setLoading(false);
|
||||
setRecords(users.sort(usersSort));
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setLoading(false);
|
||||
setErrorSnackMessage(err);
|
||||
});
|
||||
}, [setLoading, setRecords, setErrorSnackMessage]);
|
||||
|
||||
const closeAddModalAndRefresh = () => {
|
||||
setAddScreenOpen(false);
|
||||
fetchRecords();
|
||||
setLoading(true);
|
||||
};
|
||||
|
||||
const closeDeleteModalAndRefresh = (refresh: boolean) => {
|
||||
setDeleteOpen(false);
|
||||
if (refresh) {
|
||||
fetchRecords();
|
||||
setLoading(true);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -139,8 +127,21 @@ const ListUsers = ({ classes, setErrorSnackMessage, history }: IUsersProps) => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchRecords();
|
||||
}, [fetchRecords]);
|
||||
if (loading) {
|
||||
api
|
||||
.invoke("GET", `/api/v1/users`)
|
||||
.then((res: UsersList) => {
|
||||
const users = res.users === null ? [] : res.users;
|
||||
|
||||
setLoading(false);
|
||||
setRecords(users.sort(usersSort));
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setLoading(false);
|
||||
setErrorSnackMessage(err);
|
||||
});
|
||||
}
|
||||
}, [loading, setErrorSnackMessage]);
|
||||
|
||||
const filteredRecords = records.filter((elementItem) =>
|
||||
elementItem.accessKey.includes(filter)
|
||||
@@ -206,7 +207,7 @@ const ListUsers = ({ classes, setErrorSnackMessage, history }: IUsersProps) => {
|
||||
selectedGroup={null}
|
||||
closeModalAndRefresh={() => {
|
||||
setPolicyOpen(false);
|
||||
fetchRecords();
|
||||
setLoading(true);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -278,98 +279,103 @@ const ListUsers = ({ classes, setErrorSnackMessage, history }: IUsersProps) => {
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
{records.length > 0 && (
|
||||
{loading && <LinearProgress />}
|
||||
{!loading && (
|
||||
<Fragment>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[{ label: "Access Key", elementKey: "accessKey" }]}
|
||||
onSelect={selectionChanged}
|
||||
selectedItems={checkedUsers}
|
||||
isLoading={loading}
|
||||
records={filteredRecords}
|
||||
entityName="Users"
|
||||
idField="accessKey"
|
||||
customPaperHeight={classes.twHeight}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<HelpBox
|
||||
title={"Users"}
|
||||
iconComponent={<UsersIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
A MinIO user consists of a unique access key (username) and
|
||||
corresponding secret key (password). Clients must
|
||||
authenticate their identity by specifying both a valid
|
||||
access key (username) and the corresponding secret key
|
||||
(password) of an existing MinIO user.
|
||||
<br />
|
||||
<br />
|
||||
Each user can have one or more assigned policies that
|
||||
explicitly list the actions and resources to which that user
|
||||
has access. Users can also inherit policies from the groups
|
||||
in which they have membership.
|
||||
<br />
|
||||
<br />
|
||||
You can learn more at our{" "}
|
||||
<a
|
||||
href="https://docs.min.io/minio/baremetal/monitoring/bucket-notifications/bucket-notifications.html?ref=con"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
documentation
|
||||
</a>
|
||||
.
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
{records.length > 0 && (
|
||||
<Fragment>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[{ label: "Access Key", elementKey: "accessKey" }]}
|
||||
onSelect={selectionChanged}
|
||||
selectedItems={checkedUsers}
|
||||
isLoading={loading}
|
||||
records={filteredRecords}
|
||||
entityName="Users"
|
||||
idField="accessKey"
|
||||
customPaperHeight={classes.twHeight}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<HelpBox
|
||||
title={"Users"}
|
||||
iconComponent={<UsersIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
A MinIO user consists of a unique access key (username)
|
||||
and corresponding secret key (password). Clients must
|
||||
authenticate their identity by specifying both a valid
|
||||
access key (username) and the corresponding secret key
|
||||
(password) of an existing MinIO user.
|
||||
<br />
|
||||
<br />
|
||||
Each user can have one or more assigned policies that
|
||||
explicitly list the actions and resources to which that
|
||||
user has access. Users can also inherit policies from
|
||||
the groups in which they have membership.
|
||||
<br />
|
||||
<br />
|
||||
You can learn more at our{" "}
|
||||
<a
|
||||
href="https://docs.min.io/minio/baremetal/monitoring/bucket-notifications/bucket-notifications.html?ref=con"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
documentation
|
||||
</a>
|
||||
.
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)}
|
||||
{records.length === 0 && (
|
||||
<Grid
|
||||
container
|
||||
justifyContent={"center"}
|
||||
alignContent={"center"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Grid item xs={8}>
|
||||
<HelpBox
|
||||
title={"Users"}
|
||||
iconComponent={<UsersIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
A MinIO user consists of a unique access key (username)
|
||||
and corresponding secret key (password). Clients must
|
||||
authenticate their identity by specifying both a valid
|
||||
access key (username) and the corresponding secret key
|
||||
(password) of an existing MinIO user.
|
||||
<br />
|
||||
<br />
|
||||
Each user can have one or more assigned policies that
|
||||
explicitly list the actions and resources to which that
|
||||
user has access. Users can also inherit policies from
|
||||
the groups in which they have membership.
|
||||
<br />
|
||||
<br />
|
||||
To get started,{" "}
|
||||
<button
|
||||
onClick={() => {
|
||||
setAddScreenOpen(true);
|
||||
setSelectedUser(null);
|
||||
}}
|
||||
className={classes.link}
|
||||
>
|
||||
Create a User
|
||||
</button>
|
||||
.
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
{records.length == 0 && (
|
||||
<Grid
|
||||
container
|
||||
justifyContent={"center"}
|
||||
alignContent={"center"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Grid item xs={8}>
|
||||
<HelpBox
|
||||
title={"Users"}
|
||||
iconComponent={<UsersIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
A MinIO user consists of a unique access key (username) and
|
||||
corresponding secret key (password). Clients must
|
||||
authenticate their identity by specifying both a valid
|
||||
access key (username) and the corresponding secret key
|
||||
(password) of an existing MinIO user.
|
||||
<br />
|
||||
<br />
|
||||
Each user can have one or more assigned policies that
|
||||
explicitly list the actions and resources to which that user
|
||||
has access. Users can also inherit policies from the groups
|
||||
in which they have membership.
|
||||
<br />
|
||||
<br />
|
||||
To get started,{" "}
|
||||
<a
|
||||
onClick={() => {
|
||||
setAddScreenOpen(true);
|
||||
setSelectedUser(null);
|
||||
}}
|
||||
className={classes.link}
|
||||
>
|
||||
Create a User
|
||||
</a>
|
||||
.
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user