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:
Alex
2021-11-01 21:46:45 -06:00
committed by GitHub
parent e7f1aeff94
commit 684f089f61
10 changed files with 575 additions and 543 deletions

View File

@@ -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>
);

View File

@@ -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>
);

View File

@@ -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,&nbsp;
<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,&nbsp;
<button
className={classes.link}
onClick={() => {
addBucketOpen(true);
}}
>
Create a Bucket.
</button>
</Fragment>
}
/>
</Grid>
</Grid>
</Grid>
)}
</Grid>
)}
</Grid>
)}
</Grid>
</Grid>
</Fragment>

View File

@@ -965,3 +965,13 @@ export const commonDashboardInfocard = {
},
},
};
export const linkStyles = (color: string) => ({
link: {
textDecoration: "underline",
color,
backgroundColor: "transparent",
border: 0,
cursor: "pointer",
},
});

View File

@@ -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>
);

View File

@@ -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>

View File

@@ -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";

View File

@@ -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>
);

View File

@@ -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,&nbsp;
<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,&nbsp;
<button
className={classes.link}
onClick={() => {
history.push("/tenants/add");
}}
>
Create a Tenant.
</button>
</Fragment>
}
/>
</Grid>
</Grid>
)}
</Fragment>
)}
</Grid>
</Grid>

View File

@@ -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>
);