From 524c38f69ff7c2692858994d415b5db3cd570d12 Mon Sep 17 00:00:00 2001 From: Carla Martinez Date: Tue, 4 Jul 2023 16:14:26 +0200 Subject: [PATCH] Implement 'Refresh' button The 'Refresh' button will update the user fields in the 'Settings' section. In this case, this functionality has been applied in the 'Identity settings' subsection only. The data is retrieved from the `useGetUsersFullDataQuery` hook and adds ability to refresh user data in active user tab by utilizing RTKQuery refetch method. Signed-off-by: Petr Vobornik Signed-off-by: Carla Martinez --- src/components/UserSettings.tsx | 16 +++++--- .../UsersSections/UsersAccountSettings.tsx | 2 +- .../UsersSections/UsersContactSettings.tsx | 2 +- .../UsersSections/UsersIdentity.tsx | 4 +- src/hooks/useUserSettingsData.tsx | 40 +++++++++++++++++-- src/pages/ActiveUsers/ActiveUsersTabs.tsx | 14 +++---- .../PreservedUsers/PreservedUsersTabs.tsx | 14 +++---- src/pages/StageUsers/StageUsersTabs.tsx | 14 +++---- src/services/rpc.ts | 4 +- src/utils/userUtils.tsx | 4 +- 10 files changed, 73 insertions(+), 41 deletions(-) diff --git a/src/components/UserSettings.tsx b/src/components/UserSettings.tsx index 3b833575..cc1e9e3c 100644 --- a/src/components/UserSettings.tsx +++ b/src/components/UserSettings.tsx @@ -34,17 +34,17 @@ import UsersEmployeeInfo from "src/components/UsersSections/UsersEmployeeInfo"; import UsersAttributesSMB from "src/components/UsersSections/UsersAttributesSMB"; export interface PropsToUserSettings { - user: User; // TODO: Replace with `userData` in all subsections - onUserChange: (user: User) => void; + user: Partial; // TODO: Replace with `userData` in all subsections + onUserChange: (user: Partial) => void; metadata: Metadata; // eslint-disable-next-line @typescript-eslint/no-explicit-any - userData: any; - // eslint-disable-next-line @typescript-eslint/no-explicit-any pwPolicyData: any; // eslint-disable-next-line @typescript-eslint/no-explicit-any krbPolicyData: any; // eslint-disable-next-line @typescript-eslint/no-explicit-any certData: any; + onRefresh?: () => void; + isDataLoading?: boolean; from: "active-users" | "stage-users" | "preserved-users"; } @@ -84,7 +84,11 @@ const UserSettings = (props: PropsToUserSettings) => { const toolbarFields = [ { key: 0, - element: Refresh, + element: ( + + Refresh + + ), }, { key: 1, @@ -177,7 +181,7 @@ const UserSettings = (props: PropsToUserSettings) => { text="Identity settings" /> diff --git a/src/components/UsersSections/UsersAccountSettings.tsx b/src/components/UsersSections/UsersAccountSettings.tsx index 44dbd3a0..9f26554c 100644 --- a/src/components/UsersSections/UsersAccountSettings.tsx +++ b/src/components/UsersSections/UsersAccountSettings.tsx @@ -28,7 +28,7 @@ import ModalWithTextAreaLayout from "src/components/layouts/ModalWithTextAreaLay import CertificateMappingDataModal from "src/components/modals/CertificateMappingDataModal"; interface PropsToUsersAccountSettings { - user: User; + user: Partial; } // Generic data to pass to the Textbox adder diff --git a/src/components/UsersSections/UsersContactSettings.tsx b/src/components/UsersSections/UsersContactSettings.tsx index 5029244b..0890782f 100644 --- a/src/components/UsersSections/UsersContactSettings.tsx +++ b/src/components/UsersSections/UsersContactSettings.tsx @@ -13,7 +13,7 @@ import { User } from "src/utils/datatypes/globalDataTypes"; import SecondaryButton from "src/components/layouts/SecondaryButton"; interface PropsToUsersContactSettings { - user: User; + user: Partial; } interface TelephoneData { diff --git a/src/components/UsersSections/UsersIdentity.tsx b/src/components/UsersSections/UsersIdentity.tsx index 8e39a924..360ffea0 100644 --- a/src/components/UsersSections/UsersIdentity.tsx +++ b/src/components/UsersSections/UsersIdentity.tsx @@ -9,8 +9,8 @@ import { asRecord } from "src/utils/userUtils"; import IpaTextInput from "../Form/IpaTextInput"; interface PropsToUsersIdentity { - user: User; - onUserChange: (element: User) => void; + user: Partial; + onUserChange: (element: Partial) => void; metadata: Metadata; } diff --git a/src/hooks/useUserSettingsData.tsx b/src/hooks/useUserSettingsData.tsx index e3f2c3d5..c95c9b01 100644 --- a/src/hooks/useUserSettingsData.tsx +++ b/src/hooks/useUserSettingsData.tsx @@ -1,19 +1,25 @@ +import { useState, useEffect } from "react"; + // RPC import { - Command, useGetObjectMetadataQuery, useGetUsersFullDataQuery, } from "src/services/rpc"; -import { Metadata } from "src/utils/datatypes/globalDataTypes"; +import { Metadata, User } from "src/utils/datatypes/globalDataTypes"; type UserSettingsData = { isLoading: boolean; + isFetching: boolean; + modified: boolean; metadata: Metadata; - userData?: Record; + originalUser?: Partial; + user: Partial; + setUser: (user: Partial) => void; pwPolicyData?: Record; krbtPolicyData?: Record; certData?: Record; + refetch?: () => void; }; const useUserSettingsData = (userId: string): UserSettingsData => { @@ -26,18 +32,44 @@ const useUserSettingsData = (userId: string): UserSettingsData => { const userFullData = userFullDataQuery.data; const isFullDataLoading = userFullDataQuery.isLoading; + // Data displayed and modified by the user + const [user, setUser] = useState>({}); + useEffect(() => { + if (userFullData && !userFullDataQuery.isFetching) { + setUser({ ...userFullData.user }); + } + }, [userFullData, userFullDataQuery.isFetching]); + const settingsData = { isLoading: metadataLoading || isFullDataLoading, + isFetching: userFullDataQuery.isFetching, metadata, + user, + setUser, + refetch: userFullDataQuery.refetch, } as UserSettingsData; if (userFullData) { - settingsData.userData = userFullData.user; + settingsData.originalUser = userFullData.user; settingsData.pwPolicyData = userFullData.pwPolicy; settingsData.krbtPolicyData = userFullData.krbtPolicy; settingsData.certData = userFullData.cert; } + useEffect(() => { + if (!userFullData || !userFullData.user) { + return; + } + let modified = false; + for (const [key, value] of Object.entries(user)) { + if (userFullData.user[key] !== value) { + modified = true; + break; + } + } + settingsData.modified = modified; + }, [user, userFullData]); + return settingsData; }; diff --git a/src/pages/ActiveUsers/ActiveUsersTabs.tsx b/src/pages/ActiveUsers/ActiveUsersTabs.tsx index d48718f9..38c31a38 100644 --- a/src/pages/ActiveUsers/ActiveUsersTabs.tsx +++ b/src/pages/ActiveUsers/ActiveUsersTabs.tsx @@ -30,11 +30,10 @@ const ActiveUsersTabs = () => { // Get location (React Router DOM) and get state data const location = useLocation(); const userData: User = location.state as User; + const uid = userData.uid; - const [user, setUser] = useState(userData); - - // Make API calls needed for user Settings' data - const userSettingsData = useUserSettingsData(userData.uid); + // Data loaded from DB + const userSettingsData = useUserSettingsData(uid); // Tab const [activeTabKey, setActiveTabKey] = useState(0); @@ -88,13 +87,14 @@ const ActiveUsersTabs = () => { > diff --git a/src/pages/PreservedUsers/PreservedUsersTabs.tsx b/src/pages/PreservedUsers/PreservedUsersTabs.tsx index 2c17ed22..137a1305 100644 --- a/src/pages/PreservedUsers/PreservedUsersTabs.tsx +++ b/src/pages/PreservedUsers/PreservedUsersTabs.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; // PatternFly import { Title, @@ -29,11 +29,10 @@ const PreservedUsersTabs = () => { // Get location (React Router DOM) and get state data const location = useLocation(); const userData: User = location.state as User; + const uid = userData.uid; - const [user, setUser] = useState(userData); - - // Make API calls needed for user Settings' data - const userSettingsData = useUserSettingsData(userData.uid); + // Data loaded from DB + const userSettingsData = useUserSettingsData(uid); // Tab const [activeTabKey, setActiveTabKey] = useState(0); @@ -87,13 +86,12 @@ const PreservedUsersTabs = () => { > diff --git a/src/pages/StageUsers/StageUsersTabs.tsx b/src/pages/StageUsers/StageUsersTabs.tsx index f4902895..9852ce7e 100644 --- a/src/pages/StageUsers/StageUsersTabs.tsx +++ b/src/pages/StageUsers/StageUsersTabs.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; // PatternFly import { Title, @@ -29,11 +29,10 @@ const StageUsersTabs = () => { // Get location (React Router DOM) and get state data const location = useLocation(); const userData: User = location.state as User; + const uid = userData.uid; - const [user, setUser] = useState(userData); - - // Make API calls needed for user Settings' data - const userSettingsData = useUserSettingsData(userData.uid); + // Data loaded from DB + const userSettingsData = useUserSettingsData(uid); // Tab const [activeTabKey, setActiveTabKey] = useState(0); @@ -87,13 +86,12 @@ const StageUsersTabs = () => { > diff --git a/src/services/rpc.ts b/src/services/rpc.ts index 822eb21f..6f143d41 100644 --- a/src/services/rpc.ts +++ b/src/services/rpc.ts @@ -8,10 +8,10 @@ import { } from "@reduxjs/toolkit/query/react"; // Utils import { API_VERSION_BACKUP } from "src/utils/utils"; -import { Metadata } from "src/utils/datatypes/globalDataTypes"; +import { Metadata, User } from "src/utils/datatypes/globalDataTypes"; export type UserFullData = { - user?: Record; + user?: Partial; pwPolicy?: Record; krbtPolicy?: Record; cert?: Record; diff --git a/src/utils/userUtils.tsx b/src/utils/userUtils.tsx index c9685f70..85a73aea 100644 --- a/src/utils/userUtils.tsx +++ b/src/utils/userUtils.tsx @@ -5,8 +5,8 @@ import { User } from "src/utils/datatypes/globalDataTypes"; // - TODO: Adapt it to work with many types of data export const asRecord = ( // property: string, - element: User, - onElementChange: (element: User) => void + element: Partial, + onElementChange: (element: Partial) => void // metadata: Metadata ) => { // eslint-disable-next-line @typescript-eslint/no-explicit-any