diff --git a/src/components/MemberOf/MemberOfTableUserGroups.tsx b/src/components/MemberOf/MemberOfTableUserGroups.tsx
index 8dd18a20..22c20fe6 100644
--- a/src/components/MemberOf/MemberOfTableUserGroups.tsx
+++ b/src/components/MemberOf/MemberOfTableUserGroups.tsx
@@ -2,13 +2,15 @@ import React from "react";
// PatternFly
import { Table, Tr, Th, Td, Thead, Tbody } from "@patternfly/react-table";
// Data types
-import { UserGroupOld } from "src/utils/datatypes/globalDataTypes";
+import { UserGroup } from "src/utils/datatypes/globalDataTypes";
// Components
import SkeletonOnTableLayout from "../layouts/Skeleton/SkeletonOnTableLayout";
import EmptyBodyTable from "../tables/EmptyBodyTable";
+// Utils
+import { parseEmptyString } from "src/utils/utils";
export interface MemberOfUserGroupsTableProps {
- userGroups: UserGroupOld[];
+ userGroups: UserGroup[];
checkedItems?: string[];
onCheckItemsChange?: (checkedItems: string[]) => void;
showTableRows: boolean;
@@ -16,7 +18,7 @@ export interface MemberOfUserGroupsTableProps {
// Body
const UserGroupsTableBody = (props: {
- userGroups: UserGroupOld[];
+ userGroups: UserGroup[];
showCheckboxColumn: boolean;
checkedItems: string[];
onCheckboxChange: (checked: boolean, groupName: string) => void;
@@ -31,13 +33,13 @@ const UserGroupsTableBody = (props: {
select={{
rowIndex: index,
onSelect: (_e, isSelected) =>
- props.onCheckboxChange(isSelected, userGroup.name),
- isSelected: props.checkedItems.includes(userGroup.name),
+ props.onCheckboxChange(isSelected, userGroup.cn),
+ isSelected: props.checkedItems.includes(userGroup.cn),
}}
/>
)}
-
{userGroup.name} |
- {userGroup.gid} |
+ {userGroup.cn} |
+ {parseEmptyString(userGroup.gidnumber)} |
{userGroup.description} |
))}
diff --git a/src/components/MemberOf/MemberOfUserGroups.tsx b/src/components/MemberOf/MemberOfUserGroups.tsx
index 7bb25cb8..75a50193 100644
--- a/src/components/MemberOf/MemberOfUserGroups.tsx
+++ b/src/components/MemberOf/MemberOfUserGroups.tsx
@@ -2,9 +2,7 @@ import React from "react";
// PatternFly
import { Pagination, PaginationVariant } from "@patternfly/react-core";
// Data types
-import { UserGroupOld } from "src/utils/datatypes/globalDataTypes";
-// Redux
-import { useAppSelector } from "src/store/hooks";
+import { User, UserGroup } from "src/utils/datatypes/globalDataTypes";
// Components
import MemberOfToolbarUserGroups, {
MembershipDirection,
@@ -12,6 +10,8 @@ import MemberOfToolbarUserGroups, {
import MemberOfUserGroupsTable from "./MemberOfTableUserGroups";
import MemberOfAddModal, { AvailableItems } from "./MemberOfAddModal";
import MemberOfDeleteModal from "./MemberOfDeleteModal";
+// Hooks
+import { useUserMemberOfData } from "src/hooks/useUserMemberOfData";
function paginate(array: Type[], page: number, perPage: number): Type[] {
const startIdx = (page - 1) * perPage;
@@ -19,43 +19,76 @@ function paginate(array: Type[], page: number, perPage: number): Type[] {
return array.slice(startIdx, endIdx);
}
-interface TypeWithName {
- name: string;
+interface TypeWithCN {
+ cn: string;
}
// Filter functions to compare the available data with the data that
// the user is already member of. This is done to prevent duplicates
// (e.g: adding the same element twice).
-function filterUserGroupsData(
+function filterUserGroupsData(
list1: Array,
list2: Array
): Type[] {
// User groups
return list1.filter((item) => {
return !list2.some((itm) => {
- return item.name === itm.name;
+ return item.cn === itm.cn;
});
});
}
interface MemberOfUserGroupsProps {
- uid: string;
- usersGroupsFromUser: UserGroupOld[];
- updateUsersGroupsFromUser: (newList: UserGroupOld[]) => void;
+ user: Partial;
}
const MemberOfUserGroups = (props: MemberOfUserGroupsProps) => {
- const userGroupsFullList = useAppSelector(
- (state) => state.usergroups.userGroupList
- );
-
- const [groupsNamesSelected, setGroupsNamesSelected] = React.useState<
- string[]
+ // 'User groups' assigned to user
+ const [userGroupsFromUser, setUserGroupsFromUser] = React.useState<
+ UserGroup[]
>([]);
+ // Page indexes
const [page, setPage] = React.useState(1);
const [perPage, setPerPage] = React.useState(10);
+ const firstUserIdx = (page - 1) * perPage;
+ const lastUserIdx = page * perPage;
+
+ const uid = props.user.uid;
+
+ // API call: full list of 'User groups' available
+ const fullUserGroupsQuery = useUserMemberOfData({
+ uid,
+ firstUserIdx,
+ lastUserIdx,
+ });
+
+ const userGroupsFullList = fullUserGroupsQuery.userGroupsFullList;
+
+ // Get full data of the 'User groups' assigned to user
+ React.useEffect(() => {
+ if (!fullUserGroupsQuery.isFetching && userGroupsFullList) {
+ const userGroupsParsed: UserGroup[] = [];
+ props.user.memberof_group?.map((group) => {
+ userGroupsFullList.map((g) => {
+ if (g.cn === group) {
+ userGroupsParsed.push(g);
+ }
+ });
+ });
+ if (
+ JSON.stringify(userGroupsFromUser) !== JSON.stringify(userGroupsParsed)
+ ) {
+ setUserGroupsFromUser(userGroupsParsed);
+ }
+ }
+ }, [fullUserGroupsQuery]);
+
+ const [groupsNamesSelected, setGroupsNamesSelected] = React.useState<
+ string[]
+ >([]);
+
const [searchValue, setSearchValue] = React.useState("");
const [membershipDirection, setMembershipDirection] =
@@ -66,13 +99,13 @@ const MemberOfUserGroups = (props: MemberOfUserGroupsProps) => {
// Computed "states"
const someItemSelected = groupsNamesSelected.length > 0;
- const shownUserGroups = paginate(props.usersGroupsFromUser, page, perPage);
- const showTableRows = props.usersGroupsFromUser.length > 0;
+ const shownUserGroups = paginate(userGroupsFromUser, page, perPage);
+ const showTableRows = userGroupsFromUser.length > 0;
// Available data to be added as member of
- const userGroupsFilteredData: UserGroupOld[] = filterUserGroupsData(
+ const userGroupsFilteredData: UserGroup[] = filterUserGroupsData(
userGroupsFullList,
- props.usersGroupsFromUser
+ userGroupsFromUser
);
// Parse availableItems to AvailableItems type
@@ -80,8 +113,8 @@ const MemberOfUserGroups = (props: MemberOfUserGroupsProps) => {
const avItems: AvailableItems[] = [];
userGroupsFilteredData.map((item) => {
avItems.push({
- key: item.name,
- title: item.name,
+ key: item.cn,
+ title: item.cn,
});
});
return avItems;
@@ -93,18 +126,18 @@ const MemberOfUserGroups = (props: MemberOfUserGroupsProps) => {
const onAddUserGroup = (items: AvailableItems[]) => {
const newItems = items.map((item) => item.key);
const newGroups = userGroupsFullList.filter((group) =>
- newItems.includes(group.name)
+ newItems.includes(group.cn)
);
- const updatedGroups = props.usersGroupsFromUser.concat(newGroups);
- props.updateUsersGroupsFromUser(updatedGroups);
+ const updatedGroups = userGroupsFromUser.concat(newGroups);
+ setUserGroupsFromUser(updatedGroups);
};
// 'Delete' function
const onDeleteUserGroup = () => {
- const updatedGroups = props.usersGroupsFromUser.filter(
- (group) => !groupsNamesSelected.includes(group.name)
+ const updatedGroups = userGroupsFromUser.filter(
+ (group) => !groupsNamesSelected.includes(group.cn)
);
- props.updateUsersGroupsFromUser(updatedGroups);
+ setUserGroupsFromUser(updatedGroups);
};
return (
@@ -121,7 +154,7 @@ const MemberOfUserGroups = (props: MemberOfUserGroupsProps) => {
membershipDirection={membershipDirection}
onMembershipDirectionChange={setMembershipDirection}
helpIconEnabled={true}
- totalItems={props.usersGroupsFromUser.length}
+ totalItems={userGroupsFromUser.length}
perPage={perPage}
page={page}
onPerPageChange={setPerPage}
@@ -135,7 +168,7 @@ const MemberOfUserGroups = (props: MemberOfUserGroupsProps) => {
/>
{
availableItems={availableUserGroupsItems}
onAdd={onAddUserGroup}
onSearchTextChange={setSearchValue}
- title={"Add '" + props.uid + "' into User groups"}
+ title={"Add '" + props.user.uid + "' into User groups"}
ariaLabel="Add user of user group modal"
/>
)}
@@ -163,9 +196,9 @@ const MemberOfUserGroups = (props: MemberOfUserGroupsProps) => {
>
- groupsNamesSelected.includes(group.name)
- ) as UserGroupOld[]
+ userGroupsFromUser.filter((group) =>
+ groupsNamesSelected.includes(group.cn)
+ ) as UserGroup[]
}
showTableRows
/>
diff --git a/src/pages/ActiveUsers/UserMemberOf.tsx b/src/pages/ActiveUsers/UserMemberOf.tsx
index a9f3c3af..2a570057 100644
--- a/src/pages/ActiveUsers/UserMemberOf.tsx
+++ b/src/pages/ActiveUsers/UserMemberOf.tsx
@@ -13,7 +13,6 @@ import MemberOfToolbar from "src/components/MemberOf/MemberOfToolbarOld";
import MemberOfTable from "src/components/MemberOf/MemberOfTable";
// Data types
import {
- UserGroupOld,
Netgroup,
Roles,
HBACRules,
@@ -25,7 +24,6 @@ import { useAppSelector } from "src/store/hooks";
// Repositories
import {
- userGroupsInitialData,
netgroupsInitialData,
rolesInitialData,
hbacRulesInitialData,
@@ -36,6 +34,10 @@ import MemberOfAddModal from "src/components/MemberOf/MemberOfAddModalOld";
import MemberOfDeleteModal from "src/components/MemberOf/MemberOfDeleteModalOld";
// Wrappers
import MemberOfUserGroups from "src/components/MemberOf/MemberOfUserGroups";
+// RPC
+import { useGetUserByUidQuery } from "src/services/rpc";
+// Utils
+import { convertToString } from "src/utils/ipaObjectUtils";
interface PropsToUserMemberOf {
user: User;
@@ -43,6 +45,7 @@ interface PropsToUserMemberOf {
const UserMemberOf = (props: PropsToUserMemberOf) => {
// Retrieve each group list from Redux:
+ // TODO: Remove this when all data is taken from the C.L.
let netgroupsList = useAppSelector((state) => state.netgroups.netgroupList);
let rolesList = useAppSelector((state) => state.roles.roleList);
let hbacRulesList = useAppSelector((state) => state.hbacrules.hbacRulesList);
@@ -62,10 +65,34 @@ const UserMemberOf = (props: PropsToUserMemberOf) => {
sudoRulesList = newAvOptionsList as SudoRules[];
};
+ // Page indexes
+ const [page, setPage] = React.useState(1);
+ const [perPage, setPerPage] = React.useState(10);
+
+ // User's full data
+ const userQuery = useGetUserByUidQuery(convertToString(props.user.uid));
+
+ const userData = userQuery.data || {};
+
+ const [user, setUser] = React.useState>({});
+
+ React.useEffect(() => {
+ if (!userQuery.isFetching && userData) {
+ setUser({ ...userData });
+ }
+ }, [userData, userQuery.isFetching]);
+
+ // 'User groups' length to show in tab badge
+ const [userGroupsLength, setUserGroupLength] = React.useState(0);
+
+ React.useEffect(() => {
+ if (user && user.memberof_group) {
+ setUserGroupLength(user.memberof_group.length);
+ }
+ }, [user]);
+
// List of default dummy data (for each tab option)
- const [userGroupsRepository, setUserGroupsRepository] = useState(
- userGroupsInitialData
- );
+ // TODO: Remove when all data is adapted to the C.L.
const [netgroupsRepository, setNetgroupsRepository] =
useState(netgroupsInitialData);
const [rolesRepository, setRolesRepository] = useState(rolesInitialData);
@@ -84,6 +111,7 @@ const UserMemberOf = (props: PropsToUserMemberOf) => {
// Filter functions to compare the available data with the data that
// the user is already member of. This is done to prevent duplicates
// (e.g: adding the same element twice).
+ // TODO: Remove this when all tab are set into wrappers
const filterNetgroupsData = () => {
// Netgroups
return netgroupsList.filter((item) => {
@@ -205,11 +233,6 @@ const UserMemberOf = (props: PropsToUserMemberOf) => {
setActiveTabKey(tabIndex as number);
};
- // -- Pagination
- // TODO: Remove this when all tabs are adapted to its own wrapper
- const [page, setPage] = useState(1);
- const [perPage, setPerPage] = useState(10);
-
// Member groups displayed on the first page
const [shownNetgroupsList, setShownNetgroupsList] = useState(
netgroupsRepository.slice(0, perPage)
@@ -226,7 +249,7 @@ const UserMemberOf = (props: PropsToUserMemberOf) => {
// Update pagination
const changeMemberGroupsList = (
- value: UserGroupOld[] | Netgroup[] | Roles[] | HBACRules[] | SudoRules[]
+ value: Netgroup[] | Roles[] | HBACRules[] | SudoRules[]
) => {
switch (activeTabKey) {
case 1:
@@ -426,16 +449,12 @@ const UserMemberOf = (props: PropsToUserMemberOf) => {
User groups{" "}
- {userGroupsRepository.length}
+ {userGroupsLength}
}
>
-
+
{
return regexIPv4.test(ipAddress);
}
};
+
+/**
+ * Some values in a table might not have a specific value defined
+ *
+ * (i.e. empty string ""). This is not allowed by the table component.
+ * Therefore, this function will return "-" instead of "".
+ */
+export const parseEmptyString = (str: string) => {
+ if (str === "") {
+ return "-";
+ }
+ return str;
+};