Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -739,7 +739,6 @@ const TableWrapper = ({
)}
{hasOptions && (
<Column
headerRenderer={() => <Fragment>Options</Fragment>}
dataKey={idField}
width={optionsWidth}
headerClassName="optionsAlignment"
Expand Down
30 changes: 18 additions & 12 deletions portal-ui/src/screens/Console/Groups/DeleteGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,45 +24,51 @@ import useApi from "../Common/Hooks/useApi";
import { ConfirmDeleteIcon } from "../../../icons";

interface IDeleteGroup {
selectedGroup: string;
selectedGroups: string[];
deleteOpen: boolean;
closeDeleteModalAndRefresh: any;
setErrorSnackMessage: typeof setErrorSnackMessage;
}

const DeleteGroup = ({
selectedGroup,
selectedGroups,
deleteOpen,
closeDeleteModalAndRefresh,
setErrorSnackMessage,
}: IDeleteGroup) => {
const onDelSuccess = () => closeDeleteModalAndRefresh(true);
const onDelError = (err: ErrorResponseHandler) => setErrorSnackMessage(err);
const onDelError = (err: ErrorResponseHandler) => {
setErrorSnackMessage(err);
closeDeleteModalAndRefresh(true);
}
const onClose = () => closeDeleteModalAndRefresh(false);

const [deleteLoading, invokeDeleteApi] = useApi(onDelSuccess, onDelError);

if (!selectedGroup) {
if (!selectedGroups) {
return null;
}
const onDeleteGroup = () => {
invokeDeleteApi("DELETE", `/api/v1/group?name=${encodeURI(selectedGroup)}`);
};
const onDeleteGroups = () => {
for (let group of selectedGroups){
invokeDeleteApi("DELETE", `/api/v1/group?name=${encodeURI(group)}`);
}
};

const renderGroups = selectedGroups.map((group) => <div key={group}><b>{group}</b></div>);

return (
<ConfirmDialog
title={`Delete Group`}
title={`Delete Group${selectedGroups.length >1 ? "s": ""}`}
confirmText={"Delete"}
isOpen={deleteOpen}
titleIcon={<ConfirmDeleteIcon />}
isLoading={deleteLoading}
onConfirm={onDeleteGroup}
onConfirm={onDeleteGroups}
onClose={onClose}
confirmationContent={
<DialogContentText>
Are you sure you want to delete group
<br />
<b>{selectedGroup}</b>?
Are you sure you want to delete the following {selectedGroups.length} group{selectedGroups.length >1 ? "s?": "?"}
{renderGroups}
</DialogContentText>
}
/>
Expand Down
100 changes: 83 additions & 17 deletions portal-ui/src/screens/Console/Groups/Groups.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import Grid from "@mui/material/Grid";
import { LinearProgress } from "@mui/material";
import { AddIcon, GroupsIcon, UsersIcon } from "../../../icons";
import { LinearProgress, Box } from "@mui/material";
import { AddIcon, GroupsIcon, UsersIcon, DeleteIcon, IAMPoliciesIcon } from "../../../icons";
import { setErrorSnackMessage } from "../../../actions";
import { GroupsList } from "./types";
import { stringSort } from "../../../utils/sortFunctions";
Expand Down Expand Up @@ -79,12 +79,12 @@ const styles = (theme: Theme) =>
});

const Groups = ({ classes, setErrorSnackMessage, history }: IGroupsProps) => {
const [selectedGroup, setSelectedGroup] = useState<any>(null);
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
const [loading, isLoading] = useState<boolean>(false);
const [records, setRecords] = useState<any[]>([]);
const [filter, setFilter] = useState<string>("");
const [policyOpen, setPolicyOpen] = useState<boolean>(false);
const [checkedGroups, setCheckedGroups] = useState<string[]>([]);

useEffect(() => {
isLoading(true);
Expand All @@ -106,6 +106,24 @@ const Groups = ({ classes, setErrorSnackMessage, history }: IGroupsProps) => {
IAM_SCOPES.ADMIN_GET_GROUP,
]);

const selectionChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
const { target: { value = "", checked = false } = {} } = e;

let elements: string[] = [...checkedGroups]; // We clone the checkedUsers array

if (checked) {
// If the user has checked this field we need to push this to checkedUsersList
elements.push(value);
} else {
// User has unchecked this field, we need to remove it from the list
elements = elements.filter((element) => element !== value);
}

setCheckedGroups(elements);

return elements;
};

useEffect(() => {
if (loading) {
if (displayGroups) {
Expand Down Expand Up @@ -134,7 +152,7 @@ const Groups = ({ classes, setErrorSnackMessage, history }: IGroupsProps) => {

const closeDeleteModalAndRefresh = (refresh: boolean) => {
setDeleteOpen(false);

setCheckedGroups([]);
if (refresh) {
isLoading(true);
}
Expand All @@ -148,10 +166,6 @@ const Groups = ({ classes, setErrorSnackMessage, history }: IGroupsProps) => {
history.push(`${IAM_PAGES.GROUPS}/${group}`);
};

const deleteAction = (group: any) => {
setDeleteOpen(true);
setSelectedGroup(group);
};

const tableActions = [
{
Expand All @@ -160,25 +174,26 @@ const Groups = ({ classes, setErrorSnackMessage, history }: IGroupsProps) => {
disableButtonFunction: () => !getGroup,
},
{
type: "delete",
onClick: deleteAction,
disableButtonFunction: () => !deleteGroup,
type: "edit",
onClick: viewAction,
disableButtonFunction: () => !getGroup,
},
];


return (
<React.Fragment>
{deleteOpen && (
<DeleteGroup
deleteOpen={deleteOpen}
selectedGroup={selectedGroup}
selectedGroups={checkedGroups}
closeDeleteModalAndRefresh={closeDeleteModalAndRefresh}
/>
)}
{setPolicyOpen && (
{policyOpen && (
<SetPolicy
open={policyOpen}
selectedGroup={selectedGroup}
selectedGroup={checkedGroups[0]}
selectedUser={null}
closeModalAndRefresh={() => {
setPolicyOpen(false);
Expand All @@ -189,19 +204,67 @@ const Groups = ({ classes, setErrorSnackMessage, history }: IGroupsProps) => {

<PageLayout>
<Grid item xs={12} className={classes.actionsTray}>

<SecureComponent
resource={CONSOLE_UI_RESOURCE}
scopes={[IAM_SCOPES.ADMIN_LIST_GROUPS]}
errorProps={{ disabled: true }}
>
>
<SearchBox
placeholder={"Search Groups"}
onChange={setFilter}
overrideClass={classes.searchField}
value={filter}
/>
</SecureComponent>

</SecureComponent>
<Box
sx={{
display: "flex",
}}
>
{" "}
<SecureComponent
resource={CONSOLE_UI_RESOURCE}
scopes={[
IAM_SCOPES.ADMIN_ATTACH_USER_OR_GROUP_POLICY
]}
matchAll
errorProps={{ disabled: true }}
>
<RBIconButton
tooltip={"Select Policy"}
onClick={() => {
setPolicyOpen(true);
}}
text={"Assign Policy"}
icon={<IAMPoliciesIcon />}
color="primary"
disabled={checkedGroups.length !== 1}
variant={"outlined"}
/>

</SecureComponent>
<SecureComponent
resource={CONSOLE_UI_RESOURCE}
scopes={[
IAM_SCOPES.ADMIN_REMOVE_USER_FROM_GROUP
]}
matchAll
errorProps={{ disabled: true }}
>
<RBIconButton
tooltip={"Delete Selected"}
onClick={() => {
setDeleteOpen(true);
}}
text={"Delete Selected"}
icon={<DeleteIcon />}
color="secondary"
disabled={checkedGroups.length === 0}
variant={"outlined"}
/>

</SecureComponent>
<SecureComponent
resource={CONSOLE_UI_RESOURCE}
scopes={[
Expand All @@ -222,6 +285,7 @@ const Groups = ({ classes, setErrorSnackMessage, history }: IGroupsProps) => {
}}
/>
</SecureComponent>
</Box>
</Grid>
{loading && <LinearProgress />}
{!loading && (
Expand All @@ -238,6 +302,8 @@ const Groups = ({ classes, setErrorSnackMessage, history }: IGroupsProps) => {
itemActions={tableActions}
columns={[{ label: "Name", elementKey: "" }]}
isLoading={loading}
selectedItems={checkedGroups}
onSelect={deleteGroup ? selectionChanged : undefined}
records={filteredRecords}
entityName="Groups"
idField=""
Expand Down
2 changes: 1 addition & 1 deletion portal-ui/src/screens/Console/Groups/GroupsDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ const GroupsDetails = ({ classes }: IGroupDetailsProps) => {
{deleteOpen && (
<DeleteGroup
deleteOpen={deleteOpen}
selectedGroup={groupName}
selectedGroups={[groupName]}
closeDeleteModalAndRefresh={(isDelSuccess: boolean) => {
setDeleteOpen(false);
if (isDelSuccess) {
Expand Down