From 684f089f61d56210a272372dd7ee012d952ca06c Mon Sep 17 00:00:00 2001 From: Alex <33497058+bexsoft@users.noreply.github.com> Date: Mon, 1 Nov 2021 21:46:45 -0600 Subject: [PATCH] Added loading validation to avoid flashing empty components (#1177) Signed-off-by: Benjamin Perez Co-authored-by: Benjamin Perez --- .../BucketDetails/BucketEventsPanel.tsx | 52 ++-- .../BucketDetails/BucketLifecyclePanel.tsx | 52 ++-- .../Buckets/ListBuckets/ListBuckets.tsx | 104 ++++---- .../FormComponents/common/styleLibrary.ts | 10 + .../ListTiersConfiguration.tsx | 239 ++++++++--------- .../src/screens/Console/Groups/Groups.tsx | 157 ++++++------ .../Console/Logs/LogSearch/LogsSearchMain.tsx | 1 - .../ListNotificationEndpoints.tsx | 177 ++++++------- .../Tenants/ListTenants/ListTenants.tsx | 86 ++++--- .../src/screens/Console/Users/ListUsers.tsx | 240 +++++++++--------- 10 files changed, 575 insertions(+), 543 deletions(-) diff --git a/portal-ui/src/screens/Console/Buckets/BucketDetails/BucketEventsPanel.tsx b/portal-ui/src/screens/Console/Buckets/BucketDetails/BucketEventsPanel.tsx index b2472b4ba..0b7fb6bc2 100644 --- a/portal-ui/src/screens/Console/Buckets/BucketDetails/BucketEventsPanel.tsx +++ b/portal-ui/src/screens/Console/Buckets/BucketDetails/BucketEventsPanel.tsx @@ -168,31 +168,33 @@ const BucketEventsPanel = ({ customPaperHeight={classes.twHeight} /> - - } - help={ - - 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. -
-
- You can learn more at our{" "} - - documentation - - . -
- } - /> -
+ {!loadingEvents && ( + + } + help={ + + 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. +
+
+ You can learn more at our{" "} + + documentation + + . +
+ } + /> +
+ )} ); diff --git a/portal-ui/src/screens/Console/Buckets/BucketDetails/BucketLifecyclePanel.tsx b/portal-ui/src/screens/Console/Buckets/BucketDetails/BucketLifecyclePanel.tsx index e2c09bf72..35c248090 100644 --- a/portal-ui/src/screens/Console/Buckets/BucketDetails/BucketLifecyclePanel.tsx +++ b/portal-ui/src/screens/Console/Buckets/BucketDetails/BucketLifecyclePanel.tsx @@ -208,31 +208,33 @@ const BucketLifecyclePanel = ({ customPaperHeight={classes.twHeight} /> - - } - help={ - - 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. -
-
- You can learn more at our{" "} - - documentation - - . -
- } - /> -
+ {!loadingLifecycle && ( + + } + help={ + + 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. +
+
+ You can learn more at our{" "} + + documentation + + . +
+ } + /> +
+ )} ); diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/ListBuckets.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/ListBuckets.tsx index 87e76247b..b21e8e6f8 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/ListBuckets.tsx +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/ListBuckets.tsx @@ -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 = ({
- - {filteredRecords.map((bucket, index) => { - return ( - - ); - })} - {filteredRecords.length == 0 && ( - - - } - title={"Buckets"} - help={ - - 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. -
-
- To get started,  - { - addBucketOpen(true); - }} - > - Create a Bucket. - -
- } + {loading && } + {!loading && ( + + {filteredRecords.map((bucket, index) => { + return ( + + ); + })} + {filteredRecords.length === 0 && ( + + + } + title={"Buckets"} + help={ + + 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. +
+
+ To get started,  + +
+ } + /> +
-
- )} -
+ )} +
+ )}
diff --git a/portal-ui/src/screens/Console/Common/FormComponents/common/styleLibrary.ts b/portal-ui/src/screens/Console/Common/FormComponents/common/styleLibrary.ts index 520ca6ab7..420c07369 100644 --- a/portal-ui/src/screens/Console/Common/FormComponents/common/styleLibrary.ts +++ b/portal-ui/src/screens/Console/Common/FormComponents/common/styleLibrary.ts @@ -965,3 +965,13 @@ export const commonDashboardInfocard = { }, }, }; + +export const linkStyles = (color: string) => ({ + link: { + textDecoration: "underline", + color, + backgroundColor: "transparent", + border: 0, + cursor: "pointer", + }, +}); diff --git a/portal-ui/src/screens/Console/Configurations/TiersConfiguration/ListTiersConfiguration.tsx b/portal-ui/src/screens/Console/Configurations/TiersConfiguration/ListTiersConfiguration.tsx index ef5541730..8e6f6d610 100644 --- a/portal-ui/src/screens/Console/Configurations/TiersConfiguration/ListTiersConfiguration.tsx +++ b/portal-ui/src/screens/Console/Configurations/TiersConfiguration/ListTiersConfiguration.tsx @@ -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 = ({
- {records.length > 0 && ( + {isLoading && } + {!isLoading && ( - - { - 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} - /> - - - } - help={ - - 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. -
-
- You can learn more at our{" "} - - documentation - - . -
- } - /> -
+ {records.length > 0 && ( + + + { + 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} + /> + + + } + help={ + + 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. +
+
+ You can learn more at our{" "} + + documentation + + . +
+ } + /> +
+
+ )} + {records.length === 0 && ( + + + } + help={ + + 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. +
+
+ To get started,{" "} + + . +
+ } + /> +
+
+ )}
)} - {records.length == 0 && ( - - - } - help={ - - 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. -
-
- To get started,{" "} - - Add Tier - - . -
- } - /> -
-
- )} ); diff --git a/portal-ui/src/screens/Console/Groups/Groups.tsx b/portal-ui/src/screens/Console/Groups/Groups.tsx index 66081239e..606ccd441 100644 --- a/portal-ui/src/screens/Console/Groups/Groups.tsx +++ b/portal-ui/src/screens/Console/Groups/Groups.tsx @@ -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) => {
- {records.length > 0 && ( + {loading && } + {!loading && ( - - - - - } - help={ - - 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. -
-
- You can learn more at our{" "} - - documentation - - . -
- } - /> -
+ {records.length > 0 && ( + + + + + + } + help={ + + 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. +
+
+ You can learn more at our{" "} + + documentation + + . +
+ } + /> +
+
+ )} + {records.length === 0 && ( + + + } + help={ + + 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. +
+
+ To get started,{" "} + + . +
+ } + /> +
+
+ )}
)} - {records.length == 0 && ( - - - } - help={ - - 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. -
-
- To get started,{" "} - { - setSelectedGroup(null); - setGroupOpen(true); - }} - className={classes.link} - > - Create a Group - - . -
- } - /> -
-
- )} diff --git a/portal-ui/src/screens/Console/Logs/LogSearch/LogsSearchMain.tsx b/portal-ui/src/screens/Console/Logs/LogSearch/LogsSearchMain.tsx index 193248c0e..c66f902c9 100644 --- a/portal-ui/src/screens/Console/Logs/LogSearch/LogsSearchMain.tsx +++ b/portal-ui/src/screens/Console/Logs/LogSearch/LogsSearchMain.tsx @@ -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"; diff --git a/portal-ui/src/screens/Console/NotificationEndpoints/ListNotificationEndpoints.tsx b/portal-ui/src/screens/Console/NotificationEndpoints/ListNotificationEndpoints.tsx index 9acbbe7a3..a14237a7a 100644 --- a/portal-ui/src/screens/Console/NotificationEndpoints/ListNotificationEndpoints.tsx +++ b/portal-ui/src/screens/Console/NotificationEndpoints/ListNotificationEndpoints.tsx @@ -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 = ({
- {records.length > 0 && ( + {isLoading && } + {!isLoading && ( - - - - - } - help={ - - 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. -
-
- You can learn more at our{" "} - - documentation - - . -
- } - /> -
+ {records.length > 0 && ( + + + + + + } + help={ + + 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. +
+
+ You can learn more at our{" "} + + documentation + + . +
+ } + /> +
+
+ )} + {records.length === 0 && ( + + + } + help={ + + 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. +
+
+ To get started,{" "} + + . +
+ } + /> +
+
+ )}
)} - {records.length == 0 && ( - - - } - help={ - - 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. -
-
- To get started,{" "} - { - history.push("/notification-endpoints/add"); - }} - className={classes.link} - > - Add a Notification Target - - . -
- } - /> -
-
- )} ); diff --git a/portal-ui/src/screens/Console/Tenants/ListTenants/ListTenants.tsx b/portal-ui/src/screens/Console/Tenants/ListTenants/ListTenants.tsx index 5b7bdc8c9..e3f860e60 100644 --- a/portal-ui/src/screens/Console/Tenants/ListTenants/ListTenants.tsx +++ b/portal-ui/src/screens/Console/Tenants/ListTenants/ListTenants.tsx @@ -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) => { + - {filteredRecords.map((t) => { - return ; - })} - {filteredRecords.length == 0 && ( - - - } - title={"Tenants"} - help={ - - 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. -
-
- To get started,  - { - history.push("/tenants/add"); - }} - > - Create a Tenant. - -
- } - /> -
-
+ {isLoading && } + {!isLoading && ( + + {filteredRecords.map((t) => { + return ; + })} + {filteredRecords.length === 0 && ( + + + } + title={"Tenants"} + help={ + + 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. +
+
+ To get started,  + +
+ } + /> +
+
+ )} +
)}
diff --git a/portal-ui/src/screens/Console/Users/ListUsers.tsx b/portal-ui/src/screens/Console/Users/ListUsers.tsx index b00eae2a1..453359837 100644 --- a/portal-ui/src/screens/Console/Users/ListUsers.tsx +++ b/portal-ui/src/screens/Console/Users/ListUsers.tsx @@ -14,13 +14,19 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -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([]); - const [loading, setLoading] = useState(false); + const [loading, setLoading] = useState(true); const [addScreenOpen, setAddScreenOpen] = useState(false); const [deleteOpen, setDeleteOpen] = useState(false); const [selectedUser, setSelectedUser] = useState(null); @@ -103,31 +107,15 @@ const ListUsers = ({ classes, setErrorSnackMessage, history }: IUsersProps) => { const [checkedUsers, setCheckedUsers] = useState([]); const [policyOpen, setPolicyOpen] = useState(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) => {
- {records.length > 0 && ( + {loading && } + {!loading && ( - - - - - } - help={ - - 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. -
-
- 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. -
-
- You can learn more at our{" "} - - documentation - - . -
- } - /> -
+ {records.length > 0 && ( + + + + + + } + help={ + + 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. +
+
+ 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. +
+
+ You can learn more at our{" "} + + documentation + + . +
+ } + /> +
+
+ )} + {records.length === 0 && ( + + + } + help={ + + 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. +
+
+ 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. +
+
+ To get started,{" "} + + . +
+ } + /> +
+
+ )}
)} - {records.length == 0 && ( - - - } - help={ - - 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. -
-
- 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. -
-
- To get started,{" "} - { - setAddScreenOpen(true); - setSelectedUser(null); - }} - className={classes.link} - > - Create a User - - . -
- } - /> -
-
- )} );