add multiple group delete functionality (#185)

* add multiple group delete functionality

* fix the deleting bug and deleting during filering

* fix stans comment

* format files

* fix lint

* fix lint

* fix messages

* fix modal

* fix error mark found
This commit is contained in:
Christie Molloy 2020-10-27 19:45:35 -04:00 committed by GitHub
parent aea12169f4
commit 889f8078d2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 85 additions and 22 deletions

View file

@ -48,9 +48,9 @@ export const GroupsCreateModal = ({
await httpClient.doPost(`/admin/realms/${realm}/groups`, { await httpClient.doPost(`/admin/realms/${realm}/groups`, {
name: createGroupName, name: createGroupName,
}); });
refresh();
setIsCreateModalOpen(false); setIsCreateModalOpen(false);
setCreateGroupName(""); setCreateGroupName("");
refresh();
addAlert(t("groupCreated"), AlertVariant.success); addAlert(t("groupCreated"), AlertVariant.success);
} catch (error) { } catch (error) {
addAlert( addAlert(

View file

@ -16,6 +16,8 @@ import { useAlerts } from "../components/alert/Alerts";
export type GroupsListProps = { export type GroupsListProps = {
list?: GroupRepresentation[]; list?: GroupRepresentation[];
refresh: () => void; refresh: () => void;
tableRowSelectedArray: number[];
setTableRowSelectedArray: (tableRowSelectedArray: number[]) => void;
}; };
type FormattedData = { type FormattedData = {
@ -23,7 +25,12 @@ type FormattedData = {
selected: boolean; selected: boolean;
}; };
export const GroupsList = ({ list, refresh }: GroupsListProps) => { export const GroupsList = ({
list,
refresh,
tableRowSelectedArray,
setTableRowSelectedArray,
}: GroupsListProps) => {
const { t } = useTranslation("groups"); const { t } = useTranslation("groups");
const httpClient = useContext(HttpClientContext)!; const httpClient = useContext(HttpClientContext)!;
const columnGroupName: keyof GroupRepresentation = "name"; const columnGroupName: keyof GroupRepresentation = "name";
@ -67,7 +74,22 @@ export const GroupsList = ({ list, refresh }: GroupsListProps) => {
}); });
} else { } else {
localRow = [...formattedData]; localRow = [...formattedData];
const localTableRow = [...tableRowSelectedArray];
if (localRow[rowId].selected !== isSelected) {
localRow[rowId].selected = isSelected; localRow[rowId].selected = isSelected;
}
if (localTableRow.includes(rowId)) {
const index = localTableRow.indexOf(rowId);
if (index === 0) {
localTableRow.shift();
} else {
localTableRow.splice(index, 1);
}
setTableRowSelectedArray(localTableRow);
} else {
setTableRowSelectedArray([rowId, ...tableRowSelectedArray]);
}
setFormattedData(localRow); setFormattedData(localRow);
} }
} }
@ -89,6 +111,7 @@ export const GroupsList = ({ list, refresh }: GroupsListProps) => {
`/admin/realms/${realm}/groups/${list![rowId].id}` `/admin/realms/${realm}/groups/${list![rowId].id}`
); );
refresh(); refresh();
setTableRowSelectedArray([]);
addAlert(t("Group deleted"), AlertVariant.success); addAlert(t("Group deleted"), AlertVariant.success);
} catch (error) { } catch (error) {
addAlert(`${t("clientDeleteError")} ${error}`, AlertVariant.danger); addAlert(`${t("clientDeleteError")} ${error}`, AlertVariant.danger);

View file

@ -11,6 +11,8 @@ import {
import { TableToolbar } from "../components/table-toolbar/TableToolbar"; import { TableToolbar } from "../components/table-toolbar/TableToolbar";
import { ViewHeader } from "../components/view-header/ViewHeader"; import { ViewHeader } from "../components/view-header/ViewHeader";
import { ListEmptyState } from "../components/list-empty-state/ListEmptyState"; import { ListEmptyState } from "../components/list-empty-state/ListEmptyState";
import { RealmContext } from "../context/realm-context/RealmContext";
import { useAlerts } from "../components/alert/Alerts";
import { import {
Button, Button,
Dropdown, Dropdown,
@ -20,6 +22,7 @@ import {
PageSectionVariants, PageSectionVariants,
Spinner, Spinner,
ToolbarItem, ToolbarItem,
AlertVariant,
} from "@patternfly/react-core"; } from "@patternfly/react-core";
import "./GroupsSection.css"; import "./GroupsSection.css";
@ -27,24 +30,28 @@ export const GroupsSection = () => {
const { t } = useTranslation("groups"); const { t } = useTranslation("groups");
const httpClient = useContext(HttpClientContext)!; const httpClient = useContext(HttpClientContext)!;
const [rawData, setRawData] = useState<{ [key: string]: any }[]>(); const [rawData, setRawData] = useState<{ [key: string]: any }[]>();
const [filteredData, setFilteredData] = useState<object[]>(); const [filteredData, setFilteredData] = useState<{ [key: string]: any }[]>();
const [isKebabOpen, setIsKebabOpen] = useState(false); const [isKebabOpen, setIsKebabOpen] = useState(false);
const [createGroupName, setCreateGroupName] = useState(""); const [createGroupName, setCreateGroupName] = useState("");
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
const [tableRowSelectedArray, setTableRowSelectedArray] = useState<
Array<number>
>([]);
const columnID: keyof GroupRepresentation = "id"; const columnID: keyof GroupRepresentation = "id";
const membersLength: keyof GroupRepresentation = "membersLength"; const membersLength: keyof GroupRepresentation = "membersLength";
const columnGroupName: keyof GroupRepresentation = "name"; const columnGroupName: keyof GroupRepresentation = "name";
const { addAlert } = useAlerts();
const { realm } = useContext(RealmContext);
const loader = async () => { const loader = async () => {
const groups = await httpClient.doGet<ServerGroupsArrayRepresentation[]>( const groups = await httpClient.doGet<ServerGroupsArrayRepresentation[]>(
"/admin/realms/master/groups" `/admin/realms/${realm}/groups`
); );
const groupsData = groups.data!; const groupsData = groups.data!;
const getMembers = async (id: number) => { const getMembers = async (id: number) => {
const response = await httpClient.doGet< const response = await httpClient.doGet<
ServerGroupMembersRepresentation[] ServerGroupMembersRepresentation[]
>(`/admin/realms/master/groups/${id}/members`); >(`/admin/realms/${realm}/groups/${id}/members`);
const responseData = response.data!; const responseData = response.data!;
return responseData.length; return responseData.length;
}; };
@ -60,7 +67,7 @@ export const GroupsSection = () => {
return object; return object;
} }
); );
setFilteredData(updatedObject);
setRawData(updatedObject); setRawData(updatedObject);
}; };
@ -90,6 +97,31 @@ export const GroupsSection = () => {
setIsCreateModalOpen(!isCreateModalOpen); setIsCreateModalOpen(!isCreateModalOpen);
}; };
const multiDelete = async () => {
if (tableRowSelectedArray.length !== 0) {
const deleteGroup = async (rowId: number) => {
try {
await httpClient.doDelete(
`/admin/realms/${realm}/groups/${
filteredData ? filteredData![rowId].id : rawData![rowId].id
}`
);
loader();
} catch (error) {
addAlert(`${t("groupDeleteError")} ${error}`, AlertVariant.danger);
}
};
const chainedPromises = tableRowSelectedArray.map((rowId: number) => {
deleteGroup(rowId);
});
await Promise.all(chainedPromises)
.then(() => addAlert(t("groupsDeleted"), AlertVariant.success))
.then(() => setTableRowSelectedArray([]));
}
};
return ( return (
<> <>
<ViewHeader titleKey="groups:groups" subKey="groups:groupsDescription" /> <ViewHeader titleKey="groups:groups" subKey="groups:groupsDescription" />
@ -122,8 +154,12 @@ export const GroupsSection = () => {
isOpen={isKebabOpen} isOpen={isKebabOpen}
isPlain isPlain
dropdownItems={[ dropdownItems={[
<DropdownItem key="action" component="button"> <DropdownItem
{t("delete")} key="action"
component="button"
onClick={() => multiDelete()}
>
{t("common:Delete")}
</DropdownItem>, </DropdownItem>,
]} ]}
/> />
@ -135,6 +171,8 @@ export const GroupsSection = () => {
<GroupsList <GroupsList
list={filteredData ? filteredData : rawData} list={filteredData ? filteredData : rawData}
refresh={loader} refresh={loader}
tableRowSelectedArray={tableRowSelectedArray}
setTableRowSelectedArray={setTableRowSelectedArray}
/> />
)} )}
{filteredData && filteredData.length === 0 && ( {filteredData && filteredData.length === 0 && (
@ -146,6 +184,16 @@ export const GroupsSection = () => {
/> />
)} )}
</TableToolbar> </TableToolbar>
</>
) : (
<ListEmptyState
hasIcon={true}
message={t("noGroupsInThisRealm")}
instructions={t("noGroupsInThisRealmInstructions")}
primaryActionText={t("createGroup")}
onPrimaryAction={() => handleModalToggle()}
/>
)}
<GroupsCreateModal <GroupsCreateModal
isCreateModalOpen={isCreateModalOpen} isCreateModalOpen={isCreateModalOpen}
handleModalToggle={handleModalToggle} handleModalToggle={handleModalToggle}
@ -154,15 +202,6 @@ export const GroupsSection = () => {
setCreateGroupName={setCreateGroupName} setCreateGroupName={setCreateGroupName}
refresh={loader} refresh={loader}
/> />
</>
) : (
<ListEmptyState
hasIcon={true}
message={t("noGroupsInThisRealm")}
instructions={t("noGroupsInThisRealmInstructions")}
primaryActionText={t("createGroup")}
/>
)}
</PageSection> </PageSection>
</> </>
); );

View file

@ -8,7 +8,6 @@
"search": "Search", "search": "Search",
"members": "Members", "members": "Members",
"moveTo": "Move to", "moveTo": "Move to",
"delete": "Delete",
"tableOfGroups": "Table of groups", "tableOfGroups": "Table of groups",
"name": "Name", "name": "Name",
"groupsDescription": "Description goes here", "groupsDescription": "Description goes here",
@ -18,7 +17,9 @@
"create": "Create", "create": "Create",
"noSearchResults": "No search results", "noSearchResults": "No search results",
"noSearchResultsInstructions": "Click on the search bar above to search for groups", "noSearchResultsInstructions": "Click on the search bar above to search for groups",
"noGroupsInThisRealm" : "No groups in this Realm", "noGroupsInThisRealm": "No groups in this realm",
"noGroupsInThisRealmInstructions" : "You haven't created any groups in this realm. Create a group to get started." "noGroupsInThisRealmInstructions": "You haven't created any groups in this realm. Create a group to get started.",
"groupsDeleted": "Groups deleted",
"groupDeleteError": "Error deleting group"
} }
} }