Groups page ux refactor (#1183)
This commit is contained in:
committed by
GitHub
parent
acd785dfe0
commit
4e7559f354
@@ -33,6 +33,7 @@ var (
|
||||
users = "/users"
|
||||
usersDetail = "/users/:userName+"
|
||||
groups = "/groups"
|
||||
groupsDetails = "/groups/:groupName+"
|
||||
iamPolicies = "/policies"
|
||||
policiesDetail = "/policies/*"
|
||||
dashboard = "/dashboard"
|
||||
@@ -317,6 +318,7 @@ var endpointRules = map[string]ConfigurationActionSet{
|
||||
users: usersActionSet,
|
||||
usersDetail: usersActionSet,
|
||||
groups: groupsActionSet,
|
||||
groupsDetails: groupsActionSet,
|
||||
iamPolicies: iamPoliciesActionSet,
|
||||
policiesDetail: iamPoliciesActionSet,
|
||||
dashboard: dashboardActionSet,
|
||||
|
||||
@@ -70,7 +70,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
|
||||
"admin:*",
|
||||
},
|
||||
},
|
||||
want: 32,
|
||||
want: 33,
|
||||
},
|
||||
{
|
||||
name: "all s3 endpoints",
|
||||
@@ -89,7 +89,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
|
||||
"s3:*",
|
||||
},
|
||||
},
|
||||
want: 34,
|
||||
want: 35,
|
||||
},
|
||||
{
|
||||
name: "Console User - default endpoints",
|
||||
|
||||
@@ -29,7 +29,7 @@ import {
|
||||
serverIsLoading,
|
||||
serverNeedsRestart,
|
||||
setMenuOpen,
|
||||
setSnackBarMessage,
|
||||
setSnackBarMessage
|
||||
} from "../../actions";
|
||||
import { ISessionResponse } from "./types";
|
||||
import { snackBarMessage } from "../../types";
|
||||
@@ -65,6 +65,7 @@ import ListTenants from "./Tenants/ListTenants/ListTenants";
|
||||
import Tools from "./Tools/Tools";
|
||||
import ErrorLogs from "./Logs/ErrorLogs/ErrorLogs";
|
||||
import LogsSearchMain from "./Logs/LogSearch/LogsSearchMain";
|
||||
import GroupsDetails from "./Groups/GroupsDetails";
|
||||
|
||||
const drawerWidth = 245;
|
||||
|
||||
@@ -74,44 +75,44 @@ const styles = (theme: Theme) =>
|
||||
display: "flex",
|
||||
"& .MuiPaper-root.MuiSnackbarContent-root": {
|
||||
borderRadius: "0px 0px 5px 5px",
|
||||
boxShadow: "none",
|
||||
},
|
||||
boxShadow: "none"
|
||||
}
|
||||
},
|
||||
toolbar: {
|
||||
background: theme.palette.background.default,
|
||||
color: "black",
|
||||
paddingRight: 24, // keep right padding when drawer closed
|
||||
paddingRight: 24 // keep right padding when drawer closed
|
||||
},
|
||||
toolbarIcon: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-end",
|
||||
padding: "0 8px",
|
||||
...theme.mixins.toolbar,
|
||||
...theme.mixins.toolbar
|
||||
},
|
||||
appBar: {
|
||||
zIndex: theme.zIndex.drawer + 1,
|
||||
transition: theme.transitions.create(["width", "margin"], {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.leavingScreen,
|
||||
}),
|
||||
duration: theme.transitions.duration.leavingScreen
|
||||
})
|
||||
},
|
||||
appBarShift: {
|
||||
marginLeft: drawerWidth,
|
||||
width: `calc(100% - ${drawerWidth}px)`,
|
||||
transition: theme.transitions.create(["width", "margin"], {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.enteringScreen,
|
||||
}),
|
||||
duration: theme.transitions.duration.enteringScreen
|
||||
})
|
||||
},
|
||||
menuButton: {
|
||||
marginRight: 36,
|
||||
marginRight: 36
|
||||
},
|
||||
menuButtonHidden: {
|
||||
display: "none",
|
||||
display: "none"
|
||||
},
|
||||
title: {
|
||||
flexGrow: 1,
|
||||
flexGrow: 1
|
||||
},
|
||||
drawerPaper: {
|
||||
position: "relative",
|
||||
@@ -119,44 +120,44 @@ const styles = (theme: Theme) =>
|
||||
width: drawerWidth,
|
||||
transition: theme.transitions.create("width", {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.enteringScreen,
|
||||
duration: theme.transitions.duration.enteringScreen
|
||||
}),
|
||||
overflowX: "hidden",
|
||||
background:
|
||||
"transparent linear-gradient(90deg, #073052 0%, #081C42 100%) 0% 0% no-repeat padding-box",
|
||||
boxShadow: "0px 3px 7px #00000014",
|
||||
boxShadow: "0px 3px 7px #00000014"
|
||||
},
|
||||
drawerPaperClose: {
|
||||
overflowX: "hidden",
|
||||
transition: theme.transitions.create("width", {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.leavingScreen,
|
||||
duration: theme.transitions.duration.leavingScreen
|
||||
}),
|
||||
width: theme.spacing(7),
|
||||
[theme.breakpoints.up("sm")]: {
|
||||
width: theme.spacing(9),
|
||||
},
|
||||
width: theme.spacing(9)
|
||||
}
|
||||
},
|
||||
content: {
|
||||
flexGrow: 1,
|
||||
height: "100vh",
|
||||
overflow: "auto",
|
||||
position: "relative",
|
||||
position: "relative"
|
||||
},
|
||||
container: {
|
||||
paddingBottom: theme.spacing(4),
|
||||
margin: 0,
|
||||
width: "100%",
|
||||
maxWidth: "initial",
|
||||
maxWidth: "initial"
|
||||
},
|
||||
paper: {
|
||||
padding: theme.spacing(2),
|
||||
display: "flex",
|
||||
overflow: "auto",
|
||||
flexDirection: "column",
|
||||
flexDirection: "column"
|
||||
},
|
||||
fixedHeight: {
|
||||
minHeight: 240,
|
||||
minHeight: 240
|
||||
},
|
||||
warningBar: {
|
||||
background: theme.palette.primary.main,
|
||||
@@ -164,13 +165,13 @@ const styles = (theme: Theme) =>
|
||||
heigh: "60px",
|
||||
widht: "100%",
|
||||
lineHeight: "60px",
|
||||
textAlign: "center",
|
||||
textAlign: "center"
|
||||
},
|
||||
progress: {
|
||||
height: "3px",
|
||||
backgroundColor: "#eaeaea",
|
||||
backgroundColor: "#eaeaea"
|
||||
},
|
||||
...snackBarCommon,
|
||||
...snackBarCommon
|
||||
});
|
||||
|
||||
interface IConsoleProps {
|
||||
@@ -189,17 +190,17 @@ interface IConsoleProps {
|
||||
}
|
||||
|
||||
const Console = ({
|
||||
classes,
|
||||
open,
|
||||
needsRestart,
|
||||
isServerLoading,
|
||||
serverNeedsRestart,
|
||||
serverIsLoading,
|
||||
session,
|
||||
loadingProgress,
|
||||
snackBarMessage,
|
||||
setSnackBarMessage,
|
||||
}: IConsoleProps) => {
|
||||
classes,
|
||||
open,
|
||||
needsRestart,
|
||||
isServerLoading,
|
||||
serverNeedsRestart,
|
||||
serverIsLoading,
|
||||
session,
|
||||
loadingProgress,
|
||||
snackBarMessage,
|
||||
setSnackBarMessage
|
||||
}: IConsoleProps) => {
|
||||
const [openSnackbar, setOpenSnackbar] = useState<boolean>(false);
|
||||
|
||||
const restartServer = () => {
|
||||
@@ -232,171 +233,175 @@ const Console = ({
|
||||
const routes = [
|
||||
{
|
||||
component: Dashboard,
|
||||
path: "/dashboard",
|
||||
path: "/dashboard"
|
||||
},
|
||||
{
|
||||
component: Metrics,
|
||||
path: "/metrics",
|
||||
path: "/metrics"
|
||||
},
|
||||
{
|
||||
component: Buckets,
|
||||
path: "/buckets",
|
||||
path: "/buckets"
|
||||
},
|
||||
{
|
||||
component: Buckets,
|
||||
path: "/buckets/*",
|
||||
path: "/buckets/*"
|
||||
},
|
||||
{
|
||||
component: Watch,
|
||||
path: "/tools/watch",
|
||||
path: "/tools/watch"
|
||||
},
|
||||
{
|
||||
component: Users,
|
||||
path: "/users/:userName+",
|
||||
path: "/users/:userName+"
|
||||
},
|
||||
{
|
||||
component: Users,
|
||||
path: "/users",
|
||||
path: "/users"
|
||||
},
|
||||
{
|
||||
component: Groups,
|
||||
path: "/groups",
|
||||
path: "/groups"
|
||||
},
|
||||
{
|
||||
component: GroupsDetails,
|
||||
path: "/groups/:groupName+"
|
||||
},
|
||||
{
|
||||
component: Policies,
|
||||
path: "/policies/*",
|
||||
path: "/policies/*"
|
||||
},
|
||||
{
|
||||
component: Policies,
|
||||
path: "/policies",
|
||||
path: "/policies"
|
||||
},
|
||||
{
|
||||
component: Heal,
|
||||
path: "/tools/heal",
|
||||
path: "/tools/heal"
|
||||
},
|
||||
{
|
||||
component: Trace,
|
||||
path: "/tools/trace",
|
||||
path: "/tools/trace"
|
||||
},
|
||||
{
|
||||
component: HealthInfo,
|
||||
path: "/tools/diagnostics",
|
||||
path: "/tools/diagnostics"
|
||||
},
|
||||
{
|
||||
component: ErrorLogs,
|
||||
path: "/tools/logs",
|
||||
path: "/tools/logs"
|
||||
},
|
||||
{
|
||||
component: LogsSearchMain,
|
||||
path: "/tools/audit-logs",
|
||||
path: "/tools/audit-logs"
|
||||
},
|
||||
{
|
||||
component: Tools,
|
||||
path: "/tools",
|
||||
path: "/tools"
|
||||
},
|
||||
{
|
||||
component: ConfigurationMain,
|
||||
path: "/settings",
|
||||
path: "/settings"
|
||||
},
|
||||
{
|
||||
component: ConfigurationMain,
|
||||
path: "/settings/:option",
|
||||
path: "/settings/:option"
|
||||
},
|
||||
{
|
||||
component: AddNotificationEndpoint,
|
||||
path: "/notification-endpoints/add/:service",
|
||||
path: "/notification-endpoints/add/:service"
|
||||
},
|
||||
{
|
||||
component: NotificationTypeSelector,
|
||||
path: "/notification-endpoints/add",
|
||||
path: "/notification-endpoints/add"
|
||||
},
|
||||
{
|
||||
component: NotificationEndpoints,
|
||||
path: "/notification-endpoints",
|
||||
path: "/notification-endpoints"
|
||||
},
|
||||
{
|
||||
component: AddTierConfiguration,
|
||||
path: "/tiers/add/:service",
|
||||
path: "/tiers/add/:service"
|
||||
},
|
||||
{
|
||||
component: TierTypeSelector,
|
||||
path: "/tiers/add",
|
||||
path: "/tiers/add"
|
||||
},
|
||||
{
|
||||
component: ListTiersConfiguration,
|
||||
path: "/tiers",
|
||||
path: "/tiers"
|
||||
},
|
||||
{
|
||||
component: Account,
|
||||
path: "/account",
|
||||
props: {
|
||||
changePassword: session.pages.includes("/account/change-password"),
|
||||
},
|
||||
changePassword: session.pages.includes("/account/change-password")
|
||||
}
|
||||
},
|
||||
{
|
||||
component: ListTenants,
|
||||
path: "/tenants",
|
||||
path: "/tenants"
|
||||
},
|
||||
{
|
||||
component: AddTenant,
|
||||
path: "/tenants/add",
|
||||
path: "/tenants/add"
|
||||
},
|
||||
{
|
||||
component: Storage,
|
||||
path: "/storage",
|
||||
path: "/storage"
|
||||
},
|
||||
{
|
||||
component: Storage,
|
||||
path: "/storage/volumes",
|
||||
path: "/storage/volumes"
|
||||
},
|
||||
{
|
||||
component: Storage,
|
||||
path: "/storage/drives",
|
||||
path: "/storage/drives"
|
||||
},
|
||||
{
|
||||
component: TenantDetails,
|
||||
path: "/namespaces/:tenantNamespace/tenants/:tenantName",
|
||||
path: "/namespaces/:tenantNamespace/tenants/:tenantName"
|
||||
},
|
||||
{
|
||||
component: Hop,
|
||||
path: "/namespaces/:tenantNamespace/tenants/:tenantName/hop",
|
||||
path: "/namespaces/:tenantNamespace/tenants/:tenantName/hop"
|
||||
},
|
||||
{
|
||||
component: TenantDetails,
|
||||
path: "/namespaces/:tenantNamespace/tenants/:tenantName/pods/:podName",
|
||||
path: "/namespaces/:tenantNamespace/tenants/:tenantName/pods/:podName"
|
||||
},
|
||||
{
|
||||
component: TenantDetails,
|
||||
path: "/namespaces/:tenantNamespace/tenants/:tenantName/summary",
|
||||
path: "/namespaces/:tenantNamespace/tenants/:tenantName/summary"
|
||||
},
|
||||
{
|
||||
component: TenantDetails,
|
||||
path: "/namespaces/:tenantNamespace/tenants/:tenantName/metrics",
|
||||
path: "/namespaces/:tenantNamespace/tenants/:tenantName/metrics"
|
||||
},
|
||||
{
|
||||
component: TenantDetails,
|
||||
path: "/namespaces/:tenantNamespace/tenants/:tenantName/pods",
|
||||
path: "/namespaces/:tenantNamespace/tenants/:tenantName/pods"
|
||||
},
|
||||
{
|
||||
component: TenantDetails,
|
||||
path: "/namespaces/:tenantNamespace/tenants/:tenantName/pools",
|
||||
path: "/namespaces/:tenantNamespace/tenants/:tenantName/pools"
|
||||
},
|
||||
{
|
||||
component: TenantDetails,
|
||||
path: "/namespaces/:tenantNamespace/tenants/:tenantName/volumes",
|
||||
path: "/namespaces/:tenantNamespace/tenants/:tenantName/volumes"
|
||||
},
|
||||
{
|
||||
component: TenantDetails,
|
||||
path: "/namespaces/:tenantNamespace/tenants/:tenantName/license",
|
||||
path: "/namespaces/:tenantNamespace/tenants/:tenantName/license"
|
||||
},
|
||||
{
|
||||
component: TenantDetails,
|
||||
path: "/namespaces/:tenantNamespace/tenants/:tenantName/security",
|
||||
path: "/namespaces/:tenantNamespace/tenants/:tenantName/security"
|
||||
},
|
||||
{
|
||||
component: License,
|
||||
path: "/license",
|
||||
},
|
||||
path: "/license"
|
||||
}
|
||||
];
|
||||
const allowedRoutes = routes.filter((route: any) => allowedPages[route.path]);
|
||||
|
||||
@@ -481,7 +486,7 @@ const Console = ({
|
||||
snackBarMessage.type === "error"
|
||||
? classes.errorSnackBar
|
||||
: ""
|
||||
}`,
|
||||
}`
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
@@ -515,14 +520,14 @@ const mapState = (state: AppState) => ({
|
||||
isServerLoading: state.system.serverIsLoading,
|
||||
session: state.console.session,
|
||||
loadingProgress: state.system.loadingProgress,
|
||||
snackBarMessage: state.system.snackBar,
|
||||
snackBarMessage: state.system.snackBar
|
||||
});
|
||||
|
||||
const connector = connect(mapState, {
|
||||
setMenuOpen,
|
||||
serverNeedsRestart,
|
||||
serverIsLoading,
|
||||
setSnackBarMessage,
|
||||
setSnackBarMessage
|
||||
});
|
||||
|
||||
export default withStyles(styles)(connector(Console));
|
||||
|
||||
121
portal-ui/src/screens/Console/Groups/AddGroupMember.tsx
Normal file
121
portal-ui/src/screens/Console/Groups/AddGroupMember.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
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 { Button } from "@mui/material";
|
||||
import api from "../../../common/api";
|
||||
import { ErrorResponseHandler } from "../../../common/types";
|
||||
import { setModalErrorSnackMessage } from "../../../actions";
|
||||
import { connect } from "react-redux";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import { modalBasic } from "../Common/FormComponents/common/styleLibrary";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
|
||||
type UserPickerModalProps = {
|
||||
classes?: any,
|
||||
title?: string
|
||||
preSelectedUsers?: string[]
|
||||
selectedGroup?: string,
|
||||
open: boolean,
|
||||
onClose: () => void,
|
||||
onSaveClick: () => void,
|
||||
groupStatus?: string,
|
||||
}
|
||||
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
buttonContainer: {
|
||||
textAlign: "right",
|
||||
marginTop: "1rem"
|
||||
},
|
||||
...modalBasic
|
||||
});
|
||||
|
||||
const AddGroupMember = ({
|
||||
classes,
|
||||
title = "",
|
||||
groupStatus = "enabled",
|
||||
preSelectedUsers = [],
|
||||
selectedGroup = "",
|
||||
open,
|
||||
onClose
|
||||
}: UserPickerModalProps) => {
|
||||
|
||||
const [selectedUsers, setSelectedUsers] = useState(preSelectedUsers);
|
||||
|
||||
function addMembersToGroup() {
|
||||
return api
|
||||
.invoke("PUT", `/api/v1/group?name=${encodeURI(selectedGroup)}`, {
|
||||
group: selectedGroup,
|
||||
members: selectedUsers,
|
||||
status: groupStatus
|
||||
|
||||
})
|
||||
.then((res) => {
|
||||
onClose();
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
onClose();
|
||||
setModalErrorSnackMessage(err);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalWrapper
|
||||
modalOpen={open}
|
||||
onClose={onClose}
|
||||
title={title}
|
||||
>
|
||||
|
||||
<PredefinedList
|
||||
label={`Selected Group`}
|
||||
content={selectedGroup}
|
||||
/>
|
||||
|
||||
<br />
|
||||
<UsersSelectors
|
||||
selectedUsers={selectedUsers}
|
||||
setSelectedUsers={setSelectedUsers}
|
||||
editMode={!selectedGroup}
|
||||
/>
|
||||
|
||||
<Grid item xs={12} className={classes.buttonContainer}>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
color="primary"
|
||||
className={classes.clearButton}
|
||||
onClick={() => {
|
||||
setSelectedUsers(preSelectedUsers);
|
||||
}}
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
|
||||
<Button
|
||||
type="button"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
addMembersToGroup();
|
||||
}}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
</ModalWrapper>
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
|
||||
const mapDispatchToProps = {
|
||||
setModalErrorSnackMessage
|
||||
};
|
||||
|
||||
const connector = connect(null, mapDispatchToProps);
|
||||
export default withStyles(styles)(connector(AddGroupMember));
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
actionsTray,
|
||||
containerForHeader,
|
||||
linkStyles,
|
||||
searchField,
|
||||
searchField
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
import { ErrorResponseHandler } from "../../../common/types";
|
||||
import api from "../../../common/api";
|
||||
@@ -42,6 +42,7 @@ import SetPolicy from "../Policies/SetPolicy";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
import SearchIcon from "../../../icons/SearchIcon";
|
||||
import HelpBox from "../../../common/HelpBox";
|
||||
import history from "../../../history";
|
||||
|
||||
interface IGroupsProps {
|
||||
classes: any;
|
||||
@@ -52,42 +53,42 @@ interface IGroupsProps {
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
seeMore: {
|
||||
marginTop: theme.spacing(3),
|
||||
marginTop: theme.spacing(3)
|
||||
},
|
||||
paper: {
|
||||
// padding: theme.spacing(2),
|
||||
display: "flex",
|
||||
overflow: "auto",
|
||||
flexDirection: "column",
|
||||
flexDirection: "column"
|
||||
},
|
||||
addSideBar: {
|
||||
width: "320px",
|
||||
padding: "20px",
|
||||
padding: "20px"
|
||||
},
|
||||
tableToolbar: {
|
||||
paddingLeft: theme.spacing(2),
|
||||
paddingRight: theme.spacing(0),
|
||||
paddingRight: theme.spacing(0)
|
||||
},
|
||||
wrapCell: {
|
||||
maxWidth: "200px",
|
||||
whiteSpace: "normal",
|
||||
wordWrap: "break-word",
|
||||
wordWrap: "break-word"
|
||||
},
|
||||
twHeight: {
|
||||
minHeight: 600,
|
||||
minHeight: 600
|
||||
},
|
||||
minTableHeader: {
|
||||
color: "#393939",
|
||||
"& tr": {
|
||||
"& th": {
|
||||
fontWeight: "bold",
|
||||
},
|
||||
},
|
||||
fontWeight: "bold"
|
||||
}
|
||||
}
|
||||
},
|
||||
...linkStyles(theme.palette.info.main),
|
||||
...actionsTray,
|
||||
...searchField,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
...containerForHeader(theme.spacing(4))
|
||||
});
|
||||
|
||||
const Groups = ({ classes, setErrorSnackMessage }: IGroupsProps) => {
|
||||
@@ -147,8 +148,7 @@ const Groups = ({ classes, setErrorSnackMessage }: IGroupsProps) => {
|
||||
);
|
||||
|
||||
const viewAction = (group: any) => {
|
||||
setGroupOpen(true);
|
||||
setSelectedGroup(group);
|
||||
history.push(`/groups/${group}`);
|
||||
};
|
||||
|
||||
const deleteAction = (group: any) => {
|
||||
@@ -156,15 +156,9 @@ const Groups = ({ classes, setErrorSnackMessage }: IGroupsProps) => {
|
||||
setSelectedGroup(group);
|
||||
};
|
||||
|
||||
const setPolicyAction = (selectionElement: any): void => {
|
||||
setPolicyOpen(true);
|
||||
setSelectedGroup(selectionElement);
|
||||
};
|
||||
|
||||
const tableActions = [
|
||||
{ type: "view", onClick: viewAction },
|
||||
{ type: "description", onClick: setPolicyAction },
|
||||
{ type: "delete", onClick: deleteAction },
|
||||
{ type: "delete", onClick: deleteAction }
|
||||
];
|
||||
|
||||
return (
|
||||
@@ -208,7 +202,7 @@ const Groups = ({ classes, setErrorSnackMessage }: IGroupsProps) => {
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
)
|
||||
}}
|
||||
onChange={(e) => {
|
||||
setFilter(e.target.value);
|
||||
@@ -319,7 +313,7 @@ const Groups = ({ classes, setErrorSnackMessage }: IGroupsProps) => {
|
||||
};
|
||||
|
||||
const mapDispatchToProps = {
|
||||
setErrorSnackMessage,
|
||||
setErrorSnackMessage
|
||||
};
|
||||
|
||||
const connector = connect(null, mapDispatchToProps);
|
||||
|
||||
341
portal-ui/src/screens/Console/Groups/GroupsDetails.tsx
Normal file
341
portal-ui/src/screens/Console/Groups/GroupsDetails.tsx
Normal file
@@ -0,0 +1,341 @@
|
||||
import React, { Fragment, useEffect, useState } from "react";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
import { Link, useParams } from "react-router-dom";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import { actionsTray, containerForHeader, searchField } from "../Common/FormComponents/common/styleLibrary";
|
||||
import { setErrorSnackMessage, setModalErrorSnackMessage } from "../../../actions";
|
||||
import { connect } from "react-redux";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
import { Button, Grid, IconButton, Tooltip } from "@mui/material";
|
||||
import ScreenTitle from "../Common/ScreenTitle/ScreenTitle";
|
||||
import { DeleteIcon, IAMPoliciesIcon, UsersIcon } from "../../../icons";
|
||||
import List from "@mui/material/List";
|
||||
import ListItem from "@mui/material/ListItem";
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import { TabPanel } from "../../shared/tabs";
|
||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||
import history from "../../../history";
|
||||
import api from "../../../common/api";
|
||||
import SetPolicy from "../Policies/SetPolicy";
|
||||
import AddGroupMember from "./AddGroupMember";
|
||||
import { ErrorResponseHandler } from "../../../common/types";
|
||||
import DeleteGroup from "./DeleteGroup";
|
||||
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
breadcrumLink: {
|
||||
textDecoration: "none",
|
||||
color: "black"
|
||||
},
|
||||
...actionsTray,
|
||||
...searchField,
|
||||
actionsTray: { ...actionsTray.actionsTray },
|
||||
...containerForHeader(theme.spacing(4))
|
||||
});
|
||||
|
||||
interface IGroupDetailsProps {
|
||||
classes: any;
|
||||
match: any;
|
||||
setErrorSnackMessage: typeof setErrorSnackMessage;
|
||||
}
|
||||
|
||||
type TabItemsProps = {
|
||||
activeTab: number,
|
||||
onTabChange: (tab: number) => void
|
||||
}
|
||||
|
||||
type DetailsHeaderProps = {
|
||||
classes: any
|
||||
}
|
||||
|
||||
type GroupInfo = {
|
||||
members?: any[]
|
||||
name?: string
|
||||
policy?: string
|
||||
status?: string
|
||||
}
|
||||
|
||||
|
||||
const TabItems = ({ activeTab, onTabChange }: TabItemsProps) => {
|
||||
|
||||
return (
|
||||
<List component="nav" dense={true}>
|
||||
<ListItem
|
||||
button
|
||||
selected={activeTab === 0}
|
||||
onClick={() => {
|
||||
onTabChange(0);
|
||||
}}
|
||||
>
|
||||
<ListItemText primary="Members" />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
button
|
||||
selected={activeTab === 1}
|
||||
onClick={() => {
|
||||
onTabChange(1);
|
||||
}}
|
||||
>
|
||||
<ListItemText primary="Policies" />
|
||||
</ListItem>
|
||||
|
||||
</List>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
export const formatPolicy = (policy: string = ""): string[] => {
|
||||
if (policy.length <= 0) return [];
|
||||
return policy.split(",");
|
||||
};
|
||||
|
||||
export const getPoliciesAsString = (policies: string[]): string => {
|
||||
return policies.join(", ");
|
||||
};
|
||||
|
||||
|
||||
const GroupDetailsHeader = ({ classes }: DetailsHeaderProps) => {
|
||||
return (
|
||||
<PageHeader
|
||||
label={
|
||||
<Fragment>
|
||||
<Link to={"/groups"} className={classes.breadcrumLink}>
|
||||
Groups
|
||||
</Link>
|
||||
</Fragment>
|
||||
}
|
||||
actions={<React.Fragment />}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const GroupsDetails = ({ classes }: IGroupDetailsProps) => {
|
||||
|
||||
const [currentTab, setCurrentTab] = useState<number>(0);
|
||||
const [groupDetails, setGroupDetails] = useState<GroupInfo>({});
|
||||
|
||||
/*Modals*/
|
||||
const [policyOpen, setPolicyOpen] = useState<boolean>(false);
|
||||
const [usersOpen, setUsersOpen] = useState<boolean>(false);
|
||||
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
||||
|
||||
const {
|
||||
groupName = ""
|
||||
} = useParams<Record<string, string>>();
|
||||
|
||||
const {
|
||||
members = [],
|
||||
policy = "",
|
||||
status: groupEnabled
|
||||
} = groupDetails;
|
||||
|
||||
useEffect(() => {
|
||||
if (groupName) {
|
||||
fetchGroupInfo();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [groupName]);
|
||||
|
||||
const groupPolicies = formatPolicy(policy);
|
||||
const isGroupEnabled = groupEnabled === "enabled";
|
||||
const memberActionText = members.length > 0 ? "Edit Members" : "Add Members";
|
||||
|
||||
function fetchGroupInfo() {
|
||||
api
|
||||
.invoke("GET", `/api/v1/group?name=${encodeURI(groupName)}`)
|
||||
.then((res: any) => {
|
||||
setGroupDetails(res);
|
||||
}).catch(() => {
|
||||
setGroupDetails({});
|
||||
});
|
||||
};
|
||||
|
||||
function toggleGroupStatus(nextStatus: boolean) {
|
||||
|
||||
return api
|
||||
.invoke("PUT", `/api/v1/group?name=${encodeURI(groupName)}`, {
|
||||
group: groupName,
|
||||
members: members,
|
||||
status: nextStatus ? "enabled" : "disabled"
|
||||
})
|
||||
.then((res) => {
|
||||
fetchGroupInfo();
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setModalErrorSnackMessage(err);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
<React.Fragment>
|
||||
<GroupDetailsHeader classes={classes} />
|
||||
<Grid container className={classes.container}>
|
||||
<Grid item xs={12}>
|
||||
<ScreenTitle
|
||||
icon={
|
||||
<Fragment>
|
||||
<UsersIcon width={40} />
|
||||
</Fragment>
|
||||
}
|
||||
title={groupName}
|
||||
subTitle={
|
||||
<Fragment>Status: {isGroupEnabled ? "Enabled" : "Disabled"}</Fragment>
|
||||
}
|
||||
actions={
|
||||
<Fragment>
|
||||
<Button
|
||||
onClick={() => {
|
||||
toggleGroupStatus(!isGroupEnabled);
|
||||
}}
|
||||
color={"primary"}
|
||||
>
|
||||
{isGroupEnabled ? "Disable" : "Enable"}
|
||||
</Button>
|
||||
<Tooltip title="Delete User">
|
||||
<IconButton
|
||||
color="primary"
|
||||
aria-label="Delete User"
|
||||
component="span"
|
||||
onClick={() => {
|
||||
setDeleteOpen(true);
|
||||
}}
|
||||
size="large"
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={2}>
|
||||
<TabItems activeTab={currentTab} onTabChange={(num) => {
|
||||
setCurrentTab(num);
|
||||
}} />
|
||||
</Grid>
|
||||
<Grid item xs={10}>
|
||||
<Grid item xs={12}>
|
||||
<TabPanel index={0} value={currentTab}>
|
||||
<div className={classes.actionsTray}>
|
||||
<h1 className={classes.sectionTitle}>Members</h1>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<UsersIcon />}
|
||||
size="medium"
|
||||
onClick={() => {
|
||||
setUsersOpen(true);
|
||||
}}
|
||||
>
|
||||
{memberActionText}
|
||||
</Button>
|
||||
</div>
|
||||
<br />
|
||||
<TableWrapper
|
||||
//itemActions={tableActions}
|
||||
columns={[{ label: "Access Key", elementKey: "" }]}
|
||||
// onSelect={selectionChanged}
|
||||
selectedItems={[]}
|
||||
isLoading={false}
|
||||
records={members}
|
||||
entityName="Users"
|
||||
idField=""
|
||||
customPaperHeight={classes.twHeight}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel index={1} value={currentTab}>
|
||||
<div className={classes.actionsTray}>
|
||||
<h1 className={classes.sectionTitle}>Policies</h1>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<IAMPoliciesIcon />}
|
||||
size="medium"
|
||||
onClick={() => {
|
||||
setPolicyOpen(true);
|
||||
}}
|
||||
>
|
||||
Set Policies
|
||||
</Button>
|
||||
</div>
|
||||
<br />
|
||||
<TableWrapper
|
||||
itemActions={[
|
||||
{
|
||||
type: "view",
|
||||
onClick: (policy) => {
|
||||
history.push(`/policies/${policy}`);
|
||||
}
|
||||
}
|
||||
]}
|
||||
columns={[{ label: "Policy", elementKey: "" }]}
|
||||
isLoading={false}
|
||||
records={groupPolicies}
|
||||
entityName="Policies"
|
||||
idField=""
|
||||
/>
|
||||
</TabPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/*Modals*/}
|
||||
{policyOpen ? (
|
||||
<SetPolicy
|
||||
open={policyOpen}
|
||||
selectedGroup={groupName}
|
||||
selectedUser={null}
|
||||
closeModalAndRefresh={() => {
|
||||
setPolicyOpen(false);
|
||||
fetchGroupInfo();
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{usersOpen ?
|
||||
<AddGroupMember
|
||||
selectedGroup={groupName}
|
||||
onSaveClick={() => {
|
||||
|
||||
}}
|
||||
title={memberActionText}
|
||||
groupStatus={groupEnabled}
|
||||
classes={classes}
|
||||
preSelectedUsers={members}
|
||||
open={usersOpen}
|
||||
onClose={() => {
|
||||
setUsersOpen(false);
|
||||
fetchGroupInfo();
|
||||
}} /> : null}
|
||||
|
||||
{deleteOpen && (
|
||||
<DeleteGroup
|
||||
deleteOpen={deleteOpen}
|
||||
selectedGroup={groupName}
|
||||
closeDeleteModalAndRefresh={(isDelSuccess: boolean) => {
|
||||
setDeleteOpen(false);
|
||||
if (isDelSuccess) {
|
||||
history.push("/groups");
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{/*Modals*/}
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
|
||||
const mapDispatchToProps = {
|
||||
setErrorSnackMessage
|
||||
};
|
||||
|
||||
const connector = connect(null, mapDispatchToProps);
|
||||
|
||||
export default withStyles(styles)(connector(GroupsDetails));
|
||||
Reference in New Issue
Block a user