Migrated Groups Module components to mds (#2923)

Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
Alex
2023-07-05 22:29:17 -06:00
committed by GitHub
parent 542b7192c3
commit f02786001c
12 changed files with 486 additions and 712 deletions

View File

@@ -358,12 +358,6 @@ export const objectBrowserCommon = {
// ** According to W3 spec, default minimum values for flex width flex-grow is "auto" (https://drafts.csswg.org/css-flexbox/#min-size-auto). So in this case we need to enforce the use of an absolute width.
// "The preferred width of a box element child containing text content is currently the text without line breaks, leading to very unintuitive width and flex calculations → declare a width on a box element child with more than a few words (ever wonder why flexbox demos are all “1,2,3”?)"
export const selectorsCommon = {
multiSelectTable: {
height: 200,
},
};
export const settingsCommon: any = {
settingsFormContainer: {
padding: 38,

View File

@@ -1,26 +1,31 @@
import React, { useState } from "react";
import UsersSelectors from "./UsersSelectors";
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
import PredefinedList from "../Common/FormComponents/PredefinedList/PredefinedList";
import Grid from "@mui/material/Grid";
import { AddMembersToGroupIcon, Button } from "mds";
// This file is part of MinIO Console Server
// Copyright (c) 2023 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import {
formFieldStyles,
modalBasic,
modalStyleUtils,
} from "../Common/FormComponents/common/styleLibrary";
import withStyles from "@mui/styles/withStyles";
import React, { useState } from "react";
import { AddMembersToGroupIcon, Button, FormLayout, Grid, ReadBox } from "mds";
import { modalStyleUtils } from "../Common/FormComponents/common/styleLibrary";
import { encodeURLString } from "../../../common/utils";
import { setModalErrorSnackMessage } from "../../../systemSlice";
import { useAppDispatch } from "../../../store";
import { api } from "api";
import { errorToHandler } from "api/errors";
import UsersSelectors from "./UsersSelectors";
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
type UserPickerModalProps = {
classes?: any;
title?: string;
preSelectedUsers?: string[];
selectedGroup?: string;
@@ -30,21 +35,7 @@ type UserPickerModalProps = {
groupStatus?: string;
};
const styles = (theme: Theme) =>
createStyles({
userSelector: {
"& .MuiPaper-root": {
padding: 0,
marginBottom: 15,
},
},
...modalStyleUtils,
...formFieldStyles,
...modalBasic,
});
const AddGroupMember = ({
classes,
title = "",
groupStatus = "enabled",
preSelectedUsers = [],
@@ -77,21 +68,17 @@ const AddGroupMember = ({
title={title}
titleIcon={<AddMembersToGroupIcon />}
>
<Grid container>
<Grid item xs={12}>
<div className={classes.formFieldRow}>
<PredefinedList label={`Selected Group`} content={selectedGroup} />
</div>
<div className={classes.userSelector}>
<UsersSelectors
selectedUsers={selectedUsers}
setSelectedUsers={setSelectedUsers}
editMode={!selectedGroup}
/>
</div>
</Grid>
</Grid>
<Grid item xs={12} className={classes.modalButtonBar}>
<FormLayout withBorders={false} containerPadding={false}>
<ReadBox label={`Selected Group`} sx={{ width: "100%" }}>
{selectedGroup}
</ReadBox>
<UsersSelectors
selectedUsers={selectedUsers}
setSelectedUsers={setSelectedUsers}
editMode={!selectedGroup}
/>
</FormLayout>
<Grid item xs={12} sx={modalStyleUtils.modalButtonBar}>
<Button
id={"reset-add-group-member"}
type="button"
@@ -116,4 +103,4 @@ const AddGroupMember = ({
);
};
export default withStyles(styles)(AddGroupMember);
export default AddGroupMember;

View File

@@ -15,40 +15,29 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useEffect, useState } from "react";
import { Theme } from "@mui/material/styles";
import { useNavigate } from "react-router-dom";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import {
formFieldStyles,
modalStyleUtils,
} from "../Common/FormComponents/common/styleLibrary";
import Grid from "@mui/material/Grid";
import { modalStyleUtils } from "../Common/FormComponents/common/styleLibrary";
import { LinearProgress } from "@mui/material";
import { BackLink, Button, CreateGroupIcon, FormLayout, PageLayout } from "mds";
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import AddGroupHelpBox from "./AddGroupHelpBox";
import UsersSelectors from "./UsersSelectors";
import { IAM_PAGES } from "../../../common/SecureComponent/permissions";
import { setErrorSnackMessage, setHelpName } from "../../../systemSlice";
import { useAppDispatch } from "../../../store";
import PageHeaderWrapper from "../Common/PageHeaderWrapper/PageHeaderWrapper";
import HelpMenu from "../HelpMenu";
import {
BackLink,
Button,
CreateGroupIcon,
FormLayout,
Grid,
InputBox,
PageLayout,
} from "mds";
import { api } from "api";
import { errorToHandler } from "api/errors";
import { IAM_PAGES } from "../../../common/SecureComponent/permissions";
import { setErrorSnackMessage, setHelpName } from "../../../systemSlice";
import { useAppDispatch } from "../../../store";
import AddGroupHelpBox from "./AddGroupHelpBox";
import UsersSelectors from "./UsersSelectors";
import PageHeaderWrapper from "../Common/PageHeaderWrapper/PageHeaderWrapper";
import HelpMenu from "../HelpMenu";
interface IAddGroupProps {
classes: any;
}
const styles = (theme: Theme) =>
createStyles({
...formFieldStyles,
...modalStyleUtils,
});
const AddGroupScreen = ({ classes }: IAddGroupProps) => {
const AddGroupScreen = () => {
const dispatch = useAppDispatch();
const navigate = useNavigate();
const [groupName, setGroupName] = useState<string>("");
@@ -101,73 +90,64 @@ const AddGroupScreen = ({ classes }: IAddGroupProps) => {
return (
<Fragment>
<Grid item xs={12}>
<PageHeaderWrapper
label={
<BackLink
label={"Groups"}
onClick={() => navigate(IAM_PAGES.GROUPS)}
<PageHeaderWrapper
label={
<BackLink
label={"Groups"}
onClick={() => navigate(IAM_PAGES.GROUPS)}
/>
}
actions={<HelpMenu />}
/>
<PageLayout>
<FormLayout
title={"Create Group"}
icon={<CreateGroupIcon />}
helpBox={<AddGroupHelpBox />}
>
<form noValidate autoComplete="off" onSubmit={setSaving}>
<InputBox
id="group-name"
name="group-name"
label="Group Name"
autoFocus={true}
value={groupName}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setGroupName(e.target.value);
}}
/>
}
actions={<HelpMenu />}
/>
<PageLayout>
<FormLayout
title={"Create Group"}
icon={<CreateGroupIcon />}
helpBox={<AddGroupHelpBox />}
>
<form noValidate autoComplete="off" onSubmit={setSaving}>
<Grid container>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="group-name"
name="group-name"
label="Group Name"
autoFocus={true}
value={groupName}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setGroupName(e.target.value);
}}
/>
</Grid>
<Grid item xs={12} className={classes.userSelector}>
<UsersSelectors
selectedUsers={selectedUsers}
setSelectedUsers={setSelectedUsers}
editMode={true}
/>
</Grid>
</Grid>
<Grid item xs={12} className={classes.modalButtonBar}>
<Button
id={"clear-group"}
type="button"
variant="regular"
style={classes.spacerRight}
onClick={resetForm}
label={"Clear"}
/>
<UsersSelectors
selectedUsers={selectedUsers}
setSelectedUsers={setSelectedUsers}
editMode={true}
/>
<Grid item xs={12} sx={modalStyleUtils.modalButtonBar}>
<Button
id={"clear-group"}
type="button"
variant="regular"
onClick={resetForm}
label={"Clear"}
/>
<Button
id={"save-group"}
type="submit"
variant="callAction"
disabled={saving || !validGroup}
label={"Save"}
/>
<Button
id={"save-group"}
type="submit"
variant="callAction"
disabled={saving || !validGroup}
label={"Save"}
/>
</Grid>
{saving && (
<Grid item xs={12}>
<LinearProgress />
</Grid>
{saving && (
<Grid item xs={12}>
<LinearProgress />
</Grid>
)}
</form>
</FormLayout>
</PageLayout>
</Grid>
)}
</form>
</FormLayout>
</PageLayout>
</Fragment>
);
};
export default withStyles(styles)(AddGroupScreen);
export default AddGroupScreen;

View File

@@ -14,17 +14,14 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import { DialogContentText } from "@mui/material";
import React, { Fragment } from "react";
import { ErrorResponseHandler } from "../../../common/types";
import ConfirmDialog from "../Common/ModalWrapper/ConfirmDialog";
import useApi from "../Common/Hooks/useApi";
import { ConfirmDeleteIcon } from "mds";
import { encodeURLString } from "../../../common/utils";
import { setErrorSnackMessage } from "../../../systemSlice";
import { useAppDispatch } from "../../../store";
import ConfirmDialog from "../Common/ModalWrapper/ConfirmDialog";
import useApi from "../Common/Hooks/useApi";
interface IDeleteGroup {
selectedGroups: string[];
@@ -72,11 +69,12 @@ const DeleteGroup = ({
onConfirm={onDeleteGroups}
onClose={onClose}
confirmationContent={
<DialogContentText>
Are you sure you want to delete the following {selectedGroups.length}{" "}
group{selectedGroups.length > 1 ? "s?" : "?"}
<Fragment>
Are you sure you want to delete the following{" "}
{selectedGroups.length === 1 ? "" : selectedGroups.length} group
{selectedGroups.length > 1 ? "s?" : "?"}
{renderGroups}
</DialogContentText>
</Fragment>
}
/>
);

View File

@@ -1,33 +0,0 @@
import React, { Fragment, useEffect } from "react";
import { IAM_PAGES } from "../../../common/SecureComponent/permissions";
import { BackLink } from "mds";
import { useNavigate } from "react-router-dom";
import PageHeaderWrapper from "../Common/PageHeaderWrapper/PageHeaderWrapper";
import HelpMenu from "../HelpMenu";
import { useAppDispatch } from "../../../store";
import { setHelpName } from "../../../systemSlice";
const GroupDetailsHeader = () => {
const navigate = useNavigate();
const dispatch = useAppDispatch();
useEffect(() => {
dispatch(setHelpName("group_details"));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<PageHeaderWrapper
label={
<Fragment>
<BackLink
label={"Groups"}
onClick={() => navigate(IAM_PAGES.GROUPS)}
/>
</Fragment>
}
actions={<HelpMenu />}
/>
);
};
export default GroupDetailsHeader;

View File

@@ -15,7 +15,6 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useEffect, useState } from "react";
import { Theme } from "@mui/material/styles";
import { useNavigate } from "react-router-dom";
import {
AddIcon,
@@ -26,22 +25,14 @@ import {
IAMPoliciesIcon,
PageLayout,
UsersIcon,
DataTable,
Grid,
Box,
} from "mds";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import Grid from "@mui/material/Grid";
import { Box, LinearProgress } from "@mui/material";
import { LinearProgress } from "@mui/material";
import { api } from "api";
import { stringSort } from "../../../utils/sortFunctions";
import {
actionsTray,
containerForHeader,
searchField,
tableStyles,
} from "../Common/FormComponents/common/styleLibrary";
import TableWrapper from "../Common/TableWrapper/TableWrapper";
import AButton from "../Common/AButton/AButton";
import SearchBox from "../Common/SearchBox";
import { actionsTray } from "../Common/FormComponents/common/styleLibrary";
import {
applyPolicyPermissions,
CONSOLE_UI_RESOURCE,
@@ -56,44 +47,23 @@ import {
hasPermission,
SecureComponent,
} from "../../../common/SecureComponent";
import { errorToHandler } from "../../../api/errors";
import withSuspense from "../Common/Components/withSuspense";
import { encodeURLString } from "../../../common/utils";
import { setErrorSnackMessage, setHelpName } from "../../../systemSlice";
import { useAppDispatch } from "../../../store";
import TooltipWrapper from "../Common/TooltipWrapper/TooltipWrapper";
import PageHeaderWrapper from "../Common/PageHeaderWrapper/PageHeaderWrapper";
import HelpMenu from "../HelpMenu";
import { api } from "api";
import { errorToHandler } from "api/errors";
import AButton from "../Common/AButton/AButton";
import SearchBox from "../Common/SearchBox";
const DeleteGroup = withSuspense(React.lazy(() => import("./DeleteGroup")));
const SetPolicy = withSuspense(
React.lazy(() => import("../Policies/SetPolicy"))
);
interface IGroupsProps {
classes: any;
openGroupModal: any;
}
const styles = (theme: Theme) =>
createStyles({
tableBlock: {
...tableStyles.tableBlock,
marginTop: 15,
},
...actionsTray,
searchField: {
...searchField.searchField,
maxWidth: 380,
},
...containerForHeader,
});
const Groups = ({ classes }: IGroupsProps) => {
const Groups = () => {
const dispatch = useAppDispatch();
const navigate = useNavigate();
@@ -230,8 +200,8 @@ const Groups = ({ classes }: IGroupsProps) => {
<PageHeaderWrapper label={"Groups"} actions={<HelpMenu />} />
<PageLayout>
<Grid container spacing={1}>
<Grid item xs={12} className={classes.actionsTray}>
<Grid container>
<Grid item xs={12} sx={actionsTray.actionsTray}>
<SecureComponent
resource={CONSOLE_UI_RESOURCE}
scopes={displayGroupsPermissions}
@@ -240,8 +210,8 @@ const Groups = ({ classes }: IGroupsProps) => {
<SearchBox
placeholder={"Search Groups"}
onChange={setFilter}
overrideClass={classes.searchField}
value={filter}
sx={{ maxWidth: 380 }}
/>
</SecureComponent>
<Box
@@ -334,15 +304,15 @@ const Groups = ({ classes }: IGroupsProps) => {
<Fragment>
{records.length > 0 && (
<Fragment>
<Grid item xs={12} className={classes.tableBlock}>
<Grid item xs={12} sx={{ marginBottom: 15 }}>
<SecureComponent
resource={CONSOLE_UI_RESOURCE}
scopes={displayGroupsPermissions}
errorProps={{ disabled: true }}
>
<TableWrapper
<DataTable
itemActions={tableActions}
columns={[{ label: "Name", elementKey: "" }]}
columns={[{ label: "Name" }]}
isLoading={loading}
selectedItems={checkedGroups}
onSelect={
@@ -354,7 +324,7 @@ const Groups = ({ classes }: IGroupsProps) => {
/>
</SecureComponent>
</Grid>
<Grid item xs={12} marginTop={"25px"}>
<Grid item xs={12}>
<HelpBox
title={"Groups"}
iconComponent={<GroupsIcon />}
@@ -382,12 +352,7 @@ const Groups = ({ classes }: IGroupsProps) => {
</Fragment>
)}
{records.length === 0 && (
<Grid
container
justifyContent={"center"}
alignContent={"center"}
alignItems={"center"}
>
<Grid container>
<Grid item xs={8}>
<HelpBox
title={"Groups"}
@@ -429,4 +394,4 @@ const Groups = ({ classes }: IGroupsProps) => {
);
};
export default withStyles(styles)(Groups);
export default Groups;

View File

@@ -1,34 +1,40 @@
// This file is part of MinIO Console Server
// Copyright (c) 2023 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useEffect, useState } from "react";
import { Theme } from "@mui/material/styles";
import { useNavigate, useParams } from "react-router-dom";
import {
AddIcon,
BackLink,
Box,
Button,
DataTable,
Grid,
GroupsIcon,
IAMPoliciesIcon,
PageLayout,
ScreenTitle,
SectionTitle,
Switch,
Tabs,
TrashIcon,
} from "mds";
import createStyles from "@mui/styles/createStyles";
import {
actionsTray,
containerForHeader,
searchField,
spacingUtils,
tableStyles,
} from "../Common/FormComponents/common/styleLibrary";
import withStyles from "@mui/styles/withStyles";
import { Grid } from "@mui/material";
import ScreenTitle from "../Common/ScreenTitle/ScreenTitle";
import TableWrapper from "../Common/TableWrapper/TableWrapper";
import SetPolicy from "../Policies/SetPolicy";
import AddGroupMember from "./AddGroupMember";
import DeleteGroup from "./DeleteGroup";
import VerticalTabs from "../Common/VerticalTabs/VerticalTabs";
import FormSwitchWrapper from "../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
import PanelTitle from "../Common/PanelTitle/PanelTitle";
import SearchBox from "../Common/SearchBox";
import { api } from "api";
import { errorToHandler } from "api/errors";
import { Group } from "api/consoleApi";
import {
addUserToGroupPermissions,
CONSOLE_UI_RESOURCE,
@@ -47,83 +53,34 @@ import {
hasPermission,
SecureComponent,
} from "../../../common/SecureComponent";
import GroupDetailsHeader from "./GroupDetailsHeader";
import { decodeURLString, encodeURLString } from "../../../common/utils";
import { setHelpName, setModalErrorSnackMessage } from "../../../systemSlice";
import { useAppDispatch } from "../../../store";
import { setSelectedPolicies } from "../Users/AddUsersSlice";
import SetPolicy from "../Policies/SetPolicy";
import AddGroupMember from "./AddGroupMember";
import DeleteGroup from "./DeleteGroup";
import SearchBox from "../Common/SearchBox";
import TooltipWrapper from "../Common/TooltipWrapper/TooltipWrapper";
import { api } from "api";
import { errorToHandler } from "api/errors";
import { Group } from "api/consoleApi";
const styles = (theme: Theme) =>
createStyles({
pageContainer: {
border: "1px solid #EAEAEA",
width: "100%",
},
statusLabel: {
fontSize: ".8rem",
marginRight: ".7rem",
},
statusValue: {
fontWeight: "bold",
fontSize: ".9rem",
marginRight: ".7rem",
},
searchField: {
...searchField.searchField,
maxWidth: 280,
},
...tableStyles,
...spacingUtils,
actionsTray: {
...actionsTray.actionsTray,
alignItems: "center",
"& h1": {
flex: 1,
},
"& button": {
marginLeft: ".8rem",
},
"@media (max-width: 900px)": {
justifyContent: "flex-end",
"& h1": {
display: "none",
},
"& button": {
whiteSpace: "nowrap",
textOverflow: "ellipsis",
},
},
},
...containerForHeader,
});
interface IGroupDetailsProps {
classes: any;
}
import HelpMenu from "../HelpMenu";
import PageHeaderWrapper from "../Common/PageHeaderWrapper/PageHeaderWrapper";
export const formatPolicy = (policy: string = ""): string[] => {
if (policy.length <= 0) return [];
return policy.split(",");
};
const GroupsDetails = ({ classes }: IGroupDetailsProps) => {
const GroupsDetails = () => {
const dispatch = useAppDispatch();
const navigate = useNavigate();
const params = useParams();
const [groupDetails, setGroupDetails] = useState<Group>({});
/*Modals*/
const [policyOpen, setPolicyOpen] = useState<boolean>(false);
const [usersOpen, setUsersOpen] = useState<boolean>(false);
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
const [memberFilter, setMemberFilter] = useState<string>("");
const [currentTab, setCurrentTab] = useState<string>("members");
const groupName = decodeURLString(params.groupName || "");
@@ -140,7 +97,7 @@ const GroupsDetails = ({ classes }: IGroupDetailsProps) => {
);
useEffect(() => {
dispatch(setHelpName("groups_members"));
dispatch(setHelpName("group_details"));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
@@ -207,73 +164,70 @@ const GroupsDetails = ({ classes }: IGroupDetailsProps) => {
}
const groupsTabContent = (
<React.Fragment>
<div
className={classes.actionsTray}
onMouseMove={() => {
dispatch(setHelpName("groups_members"));
}}
>
<PanelTitle>Members</PanelTitle>
<SearchBox
placeholder={"Search members"}
onChange={(searchText) => {
setMemberFilter(searchText);
}}
overrideClass={classes.searchField}
value={memberFilter}
/>
<SecureComponent
resource={CONSOLE_UI_RESOURCE}
scopes={addUserToGroupPermissions}
errorProps={{ disabled: true }}
>
<TooltipWrapper
tooltip={
canEditGroupMembers
? memberActionText
: permissionTooltipHelper(
createGroupPermissions,
"edit Group membership"
)
}
<Box
onMouseMove={() => {
dispatch(setHelpName("groups_members"));
}}
>
<SectionTitle
separator
sx={{ marginBottom: 15 }}
actions={
<Box
sx={{
display: "flex",
gap: 10,
}}
>
<Button
id={"add-user-group"}
label={memberActionText}
variant="callAction"
icon={<AddIcon />}
onClick={() => {
setUsersOpen(true);
<SearchBox
placeholder={"Search members"}
onChange={(searchText) => {
setMemberFilter(searchText);
}}
value={memberFilter}
sx={{
maxWidth: 280,
}}
disabled={!canEditGroupMembers}
/>
</TooltipWrapper>
</SecureComponent>
</div>
<SecureComponent
resource={CONSOLE_UI_RESOURCE}
scopes={addUserToGroupPermissions}
errorProps={{ disabled: true }}
>
<TooltipWrapper
tooltip={
canEditGroupMembers
? memberActionText
: permissionTooltipHelper(
createGroupPermissions,
"edit Group membership"
)
}
>
<Button
id={"add-user-group"}
label={memberActionText}
variant="callAction"
icon={<AddIcon />}
onClick={() => {
setUsersOpen(true);
}}
disabled={!canEditGroupMembers}
/>
</TooltipWrapper>
</SecureComponent>
</Box>
}
>
Members
</SectionTitle>
<Grid item xs={12}>
<SecureComponent
resource={CONSOLE_UI_RESOURCE}
scopes={listUsersPermissions}
errorProps={{ disabled: true }}
>
<TableWrapper
itemActions={[
{
type: "view",
onClick: (userName) => {
navigate(`${IAM_PAGES.USERS}/${encodeURLString(userName)}`);
},
disableButtonFunction: () => !viewUser,
},
]}
columns={[{ label: "Access Key", elementKey: "" }]}
selectedItems={[]}
isLoading={false}
records={filteredMembers}
entityName="Users"
idField=""
<TooltipWrapper
tooltip={
viewUser
? ""
@@ -282,59 +236,68 @@ const GroupsDetails = ({ classes }: IGroupDetailsProps) => {
"view User details"
)
}
/>
>
<DataTable
itemActions={[
{
type: "view",
onClick: (userName) => {
navigate(`${IAM_PAGES.USERS}/${encodeURLString(userName)}`);
},
disableButtonFunction: () => !viewUser,
},
]}
columns={[{ label: "Access Key" }]}
selectedItems={[]}
isLoading={false}
records={filteredMembers}
entityName="Users"
/>
</TooltipWrapper>
</SecureComponent>
</Grid>
</React.Fragment>
</Box>
);
const policiesTabContent = (
<React.Fragment>
<div
className={classes.actionsTray}
<Fragment>
<Box
onMouseMove={() => {
dispatch(setHelpName("groups_policies"));
}}
>
<PanelTitle>Policies</PanelTitle>
<TooltipWrapper
tooltip={
canSetPolicies
? "Set Policies"
: permissionTooltipHelper(
setGroupPoliciesPermissions,
"assign Policies"
)
<SectionTitle
separator
sx={{ marginBottom: 15 }}
actions={
<TooltipWrapper
tooltip={
canSetPolicies
? "Set Policies"
: permissionTooltipHelper(
setGroupPoliciesPermissions,
"assign Policies"
)
}
>
<Button
id={"set-policies"}
label={`Set Policies`}
variant="callAction"
icon={<IAMPoliciesIcon />}
onClick={() => {
setPolicyOpen(true);
}}
disabled={!canSetPolicies}
/>
</TooltipWrapper>
}
>
<Button
id={"set-policies"}
label={`Set Policies`}
variant="callAction"
icon={<IAMPoliciesIcon />}
onClick={() => {
setPolicyOpen(true);
}}
disabled={!canSetPolicies}
/>
</TooltipWrapper>
</div>
Policies
</SectionTitle>
</Box>
<Grid item xs={12}>
<TableWrapper
itemActions={[
{
type: "view",
onClick: (policy) => {
navigate(`${IAM_PAGES.POLICIES}/${encodeURLString(policy)}`);
},
disableButtonFunction: () => !canViewPolicy,
},
]}
columns={[{ label: "Policy", elementKey: "" }]}
isLoading={false}
records={groupPolicies}
entityName="Policies"
idField=""
<TooltipWrapper
tooltip={
canViewPolicy
? ""
@@ -343,95 +306,29 @@ const GroupsDetails = ({ classes }: IGroupDetailsProps) => {
"view Policy details"
)
}
/>
</Grid>
</React.Fragment>
);
return (
<React.Fragment>
<GroupDetailsHeader />
<PageLayout className={classes.pageContainer}>
<Grid item xs={12}>
<ScreenTitle
icon={
<Fragment>
<GroupsIcon width={40} />
</Fragment>
}
title={groupName}
subTitle={null}
actions={
<Fragment>
<span className={classes.statusLabel}>Group Status:</span>
<span id="group-status" className={classes.statusValue}>
{isGroupEnabled ? "Enabled" : "Disabled"}
</span>
<TooltipWrapper
tooltip={
hasPermission(
CONSOLE_UI_RESOURCE,
enableDisableGroupPermissions,
true
)
? ""
: permissionTooltipHelper(
enableDisableGroupPermissions,
"enable or disable Groups"
)
}
>
<SecureComponent
resource={CONSOLE_UI_RESOURCE}
scopes={enableDisableGroupPermissions}
errorProps={{ disabled: true }}
matchAll
>
<FormSwitchWrapper
indicatorLabels={["Enabled", "Disabled"]}
checked={isGroupEnabled}
value={"group_enabled"}
id="group-status"
name="group-status"
onChange={() => {
toggleGroupStatus(!isGroupEnabled);
}}
switchOnly
/>
</SecureComponent>
</TooltipWrapper>
<div className={classes.spacerLeft}>
<TooltipWrapper tooltip={"Delete Group"}>
<Button
id={"delete-user-group"}
variant="secondary"
icon={<TrashIcon />}
onClick={() => {
setDeleteOpen(true);
}}
/>
</TooltipWrapper>
</div>
</Fragment>
}
>
<DataTable
itemActions={[
{
type: "view",
onClick: (policy) => {
navigate(`${IAM_PAGES.POLICIES}/${encodeURLString(policy)}`);
},
disableButtonFunction: () => !canViewPolicy,
},
]}
columns={[{ label: "Policy" }]}
isLoading={false}
records={groupPolicies}
entityName="Policies"
/>
</Grid>
</TooltipWrapper>
</Grid>
</Fragment>
);
<Grid item xs={12}>
<VerticalTabs>
{{
tabConfig: { label: "Members" },
content: groupsTabContent,
}}
{{
tabConfig: { label: "Policies" },
content: policiesTabContent,
}}
</VerticalTabs>
</Grid>
</PageLayout>
{/*Modals*/}
return (
<Fragment>
{policyOpen ? (
<SetPolicy
open={policyOpen}
@@ -472,9 +369,110 @@ const GroupsDetails = ({ classes }: IGroupDetailsProps) => {
}}
/>
)}
{/*Modals*/}
</React.Fragment>
<PageHeaderWrapper
label={
<Fragment>
<BackLink
label={"Groups"}
onClick={() => navigate(IAM_PAGES.GROUPS)}
/>
</Fragment>
}
actions={<HelpMenu />}
/>
<PageLayout>
<Grid item xs={12}>
<ScreenTitle
icon={
<Fragment>
<GroupsIcon width={40} />
</Fragment>
}
title={groupName}
subTitle={null}
bottomBorder
actions={
<Box
sx={{
display: "flex",
fontSize: 14,
alignItems: "center",
gap: 15,
}}
>
<span>Group Status:</span>
<span id="group-status-label" style={{ fontWeight: "bold" }}>
{isGroupEnabled ? "Enabled" : "Disabled"}
</span>
<TooltipWrapper
tooltip={
hasPermission(
CONSOLE_UI_RESOURCE,
enableDisableGroupPermissions,
true
)
? ""
: permissionTooltipHelper(
enableDisableGroupPermissions,
"enable or disable Groups"
)
}
>
<SecureComponent
resource={CONSOLE_UI_RESOURCE}
scopes={enableDisableGroupPermissions}
errorProps={{ disabled: true }}
matchAll
>
<Switch
indicatorLabels={["Enabled", "Disabled"]}
checked={isGroupEnabled}
value={"group_enabled"}
id="group-status"
name="group-status"
onChange={() => {
toggleGroupStatus(!isGroupEnabled);
}}
switchOnly
/>
</SecureComponent>
</TooltipWrapper>
<TooltipWrapper tooltip={"Delete Group"}>
<Button
id={"delete-user-group"}
variant="secondary"
icon={<TrashIcon />}
onClick={() => {
setDeleteOpen(true);
}}
/>
</TooltipWrapper>
</Box>
}
sx={{ marginBottom: 15 }}
/>
</Grid>
<Grid item xs={12}>
<Tabs
options={[
{
tabConfig: { id: "members", label: "Members" },
content: groupsTabContent,
},
{
tabConfig: { id: "policies", label: "Policies" },
content: policiesTabContent,
},
]}
currentTabOrPath={currentTab}
onTabClick={setCurrentTab}
/>
</Grid>
</PageLayout>
</Fragment>
);
};
export default withStyles(styles)(GroupsDetails);
export default GroupsDetails;

View File

@@ -14,71 +14,24 @@
// 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, { useCallback, useEffect, useState } from "react";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { LinearProgress } from "@mui/material";
import React, { useCallback, useEffect, useState, Fragment } from "react";
import get from "lodash/get";
import Paper from "@mui/material/Paper";
import Grid from "@mui/material/Grid";
import { usersSort } from "../../../utils/sortFunctions";
import {
actionsTray,
selectorsCommon,
tableStyles,
} from "../Common/FormComponents/common/styleLibrary";
import TableWrapper from "../Common/TableWrapper/TableWrapper";
import SearchBox from "../Common/SearchBox";
import { setModalErrorSnackMessage } from "../../../systemSlice";
import { useAppDispatch } from "../../../store";
import { api } from "api";
import { errorToHandler } from "api/errors";
import { Box, DataTable, Grid } from "mds";
import { LinearProgress } from "@mui/material";
import { usersSort } from "../../../utils/sortFunctions";
import { setModalErrorSnackMessage } from "../../../systemSlice";
import { useAppDispatch } from "../../../store";
import SearchBox from "../Common/SearchBox";
interface IGroupsProps {
classes: any;
selectedUsers: string[];
setSelectedUsers: any;
editMode?: boolean;
}
const styles = (theme: Theme) =>
createStyles({
paper: {
display: "flex",
overflow: "auto",
flexDirection: "column",
// paddingTop: 15,
boxShadow: "none",
border: 0,
},
tableBlock: {
...tableStyles.tableBlock,
},
searchBox: {
flex: 1,
},
...actionsTray,
actionsTitle: {
fontSize: 14,
alignSelf: "center",
minWidth: 160,
marginRight: 10,
},
noFound: {
textAlign: "center",
padding: theme.spacing(3),
border: "1px solid #EAEAEA",
fontSize: ".9rem",
},
...selectorsCommon,
});
const UsersSelectors = ({
classes,
selectedUsers,
setSelectedUsers,
editMode = false,
@@ -146,44 +99,43 @@ const UsersSelectors = ({
);
return (
<React.Fragment>
<Grid item xs={12}>
<Paper className={classes.paper}>
{loading && <LinearProgress />}
{records !== null && records.length > 0 ? (
<React.Fragment>
<Grid item xs={12} className={classes.actionsTray}>
<label className={classes.actionsTitle}>
{editMode ? "Edit Members" : "Assign Users"}
</label>
<div className={classes.searchBox}>
<SearchBox
placeholder="Filter Users"
onChange={setFilter}
value={filter}
/>
</div>
</Grid>
<Grid item xs={12} className={classes.tableBlock}>
<TableWrapper
columns={[{ label: "Access Key", elementKey: "accessKey" }]}
onSelect={selectionChanged}
selectedItems={selUsers}
isLoading={loading}
records={filteredRecords}
entityName="Users"
idField="accessKey"
customPaperHeight={classes.multiSelectTable}
/>
</Grid>
</React.Fragment>
) : (
<div className={classes.noFound}>No Users to display</div>
)}
</Paper>
</Grid>
</React.Fragment>
<Grid item xs={12} className={"inputItem"}>
<Box>
{loading && <LinearProgress />}
{records?.length > 0 ? (
<Fragment>
<Grid item xs={12} className={"inputItem"}>
<SearchBox
label={editMode ? "Edit Members" : "Assign Users"}
placeholder="Filter Users"
onChange={setFilter}
value={filter}
/>
</Grid>
<DataTable
columns={[{ label: "Access Key", elementKey: "accessKey" }]}
onSelect={selectionChanged}
selectedItems={selUsers}
isLoading={loading}
records={filteredRecords}
entityName="Users"
idField="accessKey"
customPaperHeight={"200px"}
/>
</Fragment>
) : (
<Box
sx={{
textAlign: "center",
padding: "10px 0",
}}
>
No Users to display
</Box>
)}
</Box>
</Grid>
);
};
export default withStyles(styles)(UsersSelectors);
export default UsersSelectors;

View File

@@ -16,59 +16,28 @@
import React, { Fragment, useEffect, useState } from "react";
import get from "lodash/get";
import { Theme } from "@mui/material/styles";
import { Button } from "mds";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { useSelector } from "react-redux";
import { Button, FormLayout, ReadBox, Grid } from "mds";
import { LinearProgress } from "@mui/material";
import Grid from "@mui/material/Grid";
import {
modalBasic,
spacingUtils,
tableStyles,
} from "../Common/FormComponents/common/styleLibrary";
import { User } from "../Users/types";
import { ErrorResponseHandler } from "../../../common/types";
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
import api from "../../../common/api";
import PolicySelectors from "./PolicySelectors";
import PredefinedList from "../Common/FormComponents/PredefinedList/PredefinedList";
import { encodeURLString } from "../../../common/utils";
import { setModalErrorSnackMessage } from "../../../systemSlice";
import { AppState, useAppDispatch } from "../../../store";
import { useSelector } from "react-redux";
import { modalStyleUtils } from "../Common/FormComponents/common/styleLibrary";
import { User } from "../Users/types";
import { setSelectedPolicies } from "../Users/AddUsersSlice";
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
import PolicySelectors from "./PolicySelectors";
import api from "../../../common/api";
interface ISetPolicyProps {
classes: any;
closeModalAndRefresh: () => void;
selectedUser: User | null;
selectedGroups: string[] | null;
open: boolean;
}
const styles = (theme: Theme) =>
createStyles({
...modalBasic,
...spacingUtils,
tableBlock: {
...tableStyles.tableBlock,
marginTop: 15,
},
buttonContainer: {
display: "flex",
justifyContent: "flex-end",
marginTop: ".9rem",
"& button": {
marginLeft: 8,
},
},
});
const SetPolicy = ({
classes,
closeModalAndRefresh,
selectedUser,
selectedGroups,
@@ -158,41 +127,34 @@ const SetPolicy = ({
modalOpen={open}
title="Set Policies"
>
<Grid container>
<FormLayout withBorders={false} containerPadding={false}>
{(selectedGroups?.length === 1 || selectedUser != null) && (
<Fragment>
<Grid item xs={12}>
<PredefinedList
label={`Selected ${selectedGroups !== null ? "Group" : "User"}`}
content={selectedGroups !== null ? selectedGroups[0] : userName}
/>
</Grid>
<Grid item xs={12}>
<PredefinedList
label={"Current Policy"}
content={actualPolicy.join(", ")}
/>
</Grid>
<ReadBox
label={`Selected ${selectedGroups !== null ? "Group" : "User"}`}
sx={{ width: "100%" }}
>
{selectedGroups !== null ? selectedGroups[0] : userName}
</ReadBox>
<ReadBox label={"Current Policy"} sx={{ width: "100%" }}>
{actualPolicy.join(", ")}
</ReadBox>
</Fragment>
)}
{selectedGroups && selectedGroups?.length > 1 && (
<PredefinedList
label={"Selected Groups"}
content={selectedGroups.join(", ")}
/>
<ReadBox label={"Selected Groups"} sx={{ width: "100%" }}>
{selectedGroups.join(", ")}
</ReadBox>
)}
<Grid item xs={12}>
<div className={classes.tableBlock}>
<PolicySelectors selectedPolicy={selectedPolicy} />
</div>
<PolicySelectors selectedPolicy={selectedPolicy} />
</Grid>
</Grid>
<Grid item xs={12} className={classes.buttonContainer}>
</FormLayout>
<Grid item xs={12} sx={modalStyleUtils.modalButtonBar}>
<Button
id={"reset"}
type="button"
variant="regular"
className={classes.spacerRight}
onClick={resetSelection}
label={"Reset"}
/>
@@ -215,4 +177,4 @@ const SetPolicy = ({
);
};
export default withStyles(styles)(SetPolicy);
export default SetPolicy;

View File

@@ -15,8 +15,6 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useEffect, useState } from "react";
import { Theme } from "@mui/material/styles";
import { useNavigate } from "react-router-dom";
import {
AddIcon,
@@ -29,23 +27,12 @@ import {
DataTable,
Grid,
} from "mds";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import api from "../../../common/api";
import { LinearProgress } from "@mui/material";
import { User, UsersList } from "./types";
import { usersSort } from "../../../utils/sortFunctions";
import {
actionsTray,
containerForHeader,
searchField,
tableStyles,
} from "../Common/FormComponents/common/styleLibrary";
import { actionsTray } from "../Common/FormComponents/common/styleLibrary";
import { ErrorResponseHandler } from "../../../common/types";
import { encodeURLString } from "../../../common/utils";
import AButton from "../Common/AButton/AButton";
import SearchBox from "../Common/SearchBox";
import withSuspense from "../Common/Components/withSuspense";
import {
addUserToGroupPermissions,
CONSOLE_UI_RESOURCE,
@@ -57,6 +44,10 @@ import {
S3_ALL_RESOURCES,
viewUserPermissions,
} from "../../../common/SecureComponent/permissions";
import api from "../../../common/api";
import AButton from "../Common/AButton/AButton";
import SearchBox from "../Common/SearchBox";
import withSuspense from "../Common/Components/withSuspense";
import {
hasPermission,
@@ -71,24 +62,7 @@ import HelpMenu from "../HelpMenu";
const DeleteUser = withSuspense(React.lazy(() => import("./DeleteUser")));
const AddToGroup = withSuspense(React.lazy(() => import("./BulkAddToGroup")));
const styles = (theme: Theme) =>
createStyles({
...actionsTray,
...searchField,
searchField: {
...searchField.searchField,
marginRight: "auto",
maxWidth: 380,
},
...tableStyles,
...containerForHeader,
});
interface IUsersProps {
classes: any;
}
const ListUsers = ({ classes }: IUsersProps) => {
const ListUsers = () => {
const dispatch = useAppDispatch();
const navigate = useNavigate();
@@ -219,12 +193,15 @@ const ListUsers = ({ classes }: IUsersProps) => {
<PageLayout>
<Grid container>
<Grid item xs={12} className={classes.actionsTray}>
<Grid item xs={12} sx={actionsTray.actionsTray}>
<SearchBox
placeholder={"Search Users"}
onChange={setFilter}
overrideClass={classes.searchField}
value={filter}
sx={{
marginRight: "auto",
maxWidth: 380,
}}
/>
<SecureComponent
resource={CONSOLE_UI_RESOURCE}
@@ -352,12 +329,7 @@ const ListUsers = ({ classes }: IUsersProps) => {
<Fragment>
{records.length > 0 && (
<Fragment>
<Grid
item
xs={12}
className={classes.tableBlock}
sx={{ marginBottom: 15 }}
>
<Grid item xs={12} sx={{ marginBottom: 15 }}>
<SecureComponent
scopes={[IAM_SCOPES.ADMIN_LIST_USERS]}
resource={CONSOLE_UI_RESOURCE}
@@ -496,4 +468,4 @@ const ListUsers = ({ classes }: IUsersProps) => {
);
};
export default withStyles(styles)(ListUsers);
export default ListUsers;

View File

@@ -371,7 +371,7 @@ const UserDetails = () => {
disabled: !canAssignGroup,
},
content: (
<React.Fragment>
<Fragment>
<Box
onMouseMove={() =>
dispatch(setHelpName("user_details_groups"))
@@ -423,7 +423,7 @@ const UserDetails = () => {
idField="group"
/>
</Grid>
</React.Fragment>
</Fragment>
),
},
{

View File

@@ -52,7 +52,7 @@ export const assignPoliciesButton = Selector("button").withAttribute(
//----------------------------------------------------
// Switches
//----------------------------------------------------
export const switchInput = Selector(".MuiSwitch-input");
export const switchInput = Selector("#group-status").sibling("span");
export const deleteAllVersions =
Selector("#delete-versions").sibling("span.switchRail");
@@ -84,11 +84,10 @@ export const filterUserInput = searchResourceInput.withAttribute(
"placeholder",
"Filter Users"
);
export const groupUserCheckbox = Selector(".ReactVirtualized__Table__row span")
.withText(constants.TEST_USER_NAME)
.parent(1)
.find(".ReactVirtualized__Grid input")
.withAttribute("type", "checkbox");
export const groupUserCheckbox = Selector(".ReactVirtualized__Table__row input")
.withAttribute("type", "checkbox")
.withAttribute("value", constants.TEST_USER_NAME)
.sibling("span");
//----------------------------------------------------
// Dropdowns and options
@@ -103,7 +102,7 @@ export const bucketDropdownOptionFor = (modifier) => {
//----------------------------------------------------
// Text
//----------------------------------------------------
export const groupStatusText = Selector("#group-status");
export const groupStatusText = Selector("#group-status-label");
//----------------------------------------------------
// Tables, table headers and content