diff --git a/src/components/UserSettings.tsx b/src/components/UserSettings.tsx index da79857a..12dbc4d3 100644 --- a/src/components/UserSettings.tsx +++ b/src/components/UserSettings.tsx @@ -55,8 +55,12 @@ import UnlockUser from "./modals/UnlockUser"; import ResetPassword from "./modals/ResetPassword"; import IssueNewCertificate from "./modals/IssueNewCertificate"; import AddOtpToken from "./modals/AddOtpToken"; +import ActivateStageUsers from "./modals/ActivateStageUsers"; // Utils import { API_VERSION_BACKUP } from "src/utils/utils"; +// Navigate +import { URL_PREFIX } from "src/navigation/NavRoutes"; +import { useNavigate } from "react-router-dom"; export interface PropsToUserSettings { originalUser: Partial; @@ -82,6 +86,9 @@ const UserSettings = (props: PropsToUserSettings) => { // Alerts to show in the UI const alerts = useAlerts(); + // Navigate + const navigate = useNavigate(); + // RTK hook: save user (acive/preserved and stage) let [saveUser] = useSaveUserMutation(); if (props.from === "stage-users") { @@ -233,6 +240,12 @@ const UserSettings = (props: PropsToUserSettings) => { }); }; + // Stage users - 'Activate' option + const [isActivateModalOpen, setIsActivateModalOpen] = React.useState(false); + const onCloseActivateModal = () => { + setIsActivateModalOpen(false); + }; + // Kebab const [isKebabOpen, setIsKebabOpen] = useState(false); @@ -295,7 +308,9 @@ const UserSettings = (props: PropsToUserSettings) => { ]; const stageDropdownItems = [ - Activate, + setIsActivateModalOpen(true)}> + Activate + , setIsDeleteModalOpen(true)}> Delete , @@ -603,6 +618,12 @@ const UserSettings = (props: PropsToUserSettings) => { onClose={onCloseAddOtpTokenModal} /> )} + navigate(URL_PREFIX + "/stage-users")} + /> ); }; diff --git a/src/components/modals/ActivateStageUsers.tsx b/src/components/modals/ActivateStageUsers.tsx index 5e077ff3..d0905bbc 100644 --- a/src/components/modals/ActivateStageUsers.tsx +++ b/src/components/modals/ActivateStageUsers.tsx @@ -15,32 +15,15 @@ import UsersDisplayTable from "src/components/tables/UsersDisplayTable"; import { useAppDispatch } from "src/store/hooks"; import { removeUser as removeStageUser } from "src/store/Identity/stageUsers-slice"; // RPC -import { - Command, - BatchRPCResponse, - useBatchMutCommandMutation, -} from "src/services/rpc"; -import { FetchBaseQueryError } from "@reduxjs/toolkit/dist/query"; -import { SerializedError } from "@reduxjs/toolkit"; -// Modals -import ErrorModal from "./ErrorModal"; -// Data types -import { ErrorData } from "src/utils/datatypes/globalDataTypes"; +import { useActivateUserMutation, ErrorResult } from "src/services/rpc"; // Hooks import useAlerts from "src/hooks/useAlerts"; -interface SelectedUsersData { - selectedUsers: string[]; - updateSelectedUsers: (newSelectedUsers: string[]) => void; -} - export interface PropsToActivateUsers { show: boolean; handleModalToggle: () => void; - selectedUsersData: SelectedUsersData; - onRefresh?: () => void; - onOpenDeleteModal?: () => void; - onCloseDeleteModal?: () => void; + selectedUids: string[]; + onSuccess: () => void; } const ActivateStageUsers = (props: PropsToActivateUsers) => { @@ -51,7 +34,7 @@ const ActivateStageUsers = (props: PropsToActivateUsers) => { const alerts = useAlerts(); // Define 'executeUserStageCommand' to activate user data to IPA server - const [executeUserActivateCommand] = useBatchMutCommandMutation(); + const [activateUsersCommand] = useActivateUserMutation(); const [noMembersChecked, setNoMembers] = useState(false); @@ -71,7 +54,7 @@ const ActivateStageUsers = (props: PropsToActivateUsers) => { id: "activate-users-table", pfComponent: ( ), @@ -97,106 +80,37 @@ const ActivateStageUsers = (props: PropsToActivateUsers) => { props.handleModalToggle(); }; - // Handle API error data - const [isModalErrorOpen, setIsModalErrorOpen] = useState(false); - const [errorTitle, setErrorTitle] = useState(""); - const [errorMessage, setErrorMessage] = useState(""); - - const closeAndCleanErrorParameters = () => { - setIsModalErrorOpen(false); - setErrorTitle(""); - setErrorMessage(""); - }; - - const onCloseErrorModal = () => { - closeAndCleanErrorParameters(); - }; - - const errorModalActions = [ - , - ]; - - const handleAPIError = (error: FetchBaseQueryError | SerializedError) => { - if ("code" in error) { - setErrorTitle("IPA error " + error.code + ": " + error.name); - if (error.message !== undefined) { - setErrorMessage(error.message); - } - } else if ("data" in error) { - const errorData = error.data as ErrorData; - const errorCode = errorData.code as string; - const errorName = errorData.name as string; - const errorMessage = errorData.error as string; - - setErrorTitle("IPA error " + errorCode + ": " + errorName); - setErrorMessage(errorMessage); - } - setIsModalErrorOpen(true); - }; - // Stage user const activateUsers = () => { // Prepare users params - const uidsToActivatePayload: Command[] = []; - - props.selectedUsersData.selectedUsers.map((uid) => { - const payloadItem = { - method: "stageuser_activate", - params: [uid, { no_members: noMembersChecked }], - } as Command; - uidsToActivatePayload.push(payloadItem); - }); + const uidsToActivatePayload = props.selectedUids; // [API call] activate elements - executeUserActivateCommand(uidsToActivatePayload).then((response) => { + activateUsersCommand(uidsToActivatePayload).then((response) => { if ("data" in response) { - const data = response.data as BatchRPCResponse; - const result = data.result; - const error = data.error as FetchBaseQueryError | SerializedError; - - if (result) { - if ("error" in result.results[0] && result.results[0].error) { - const errorData = { - code: result.results[0].error_code, - name: result.results[0].error_name, - error: result.results[0].error, - } as ErrorData; - - const error = { - status: "CUSTOM_ERROR", - data: errorData, - } as FetchBaseQueryError; - - // Handle error - handleAPIError(error); - } else { - // Update data from Redux - props.selectedUsersData.selectedUsers.map((user) => { - dispatch(removeStageUser(user[0])); - }); - - // Reset selected values - props.selectedUsersData.updateSelectedUsers([]); - - // Refresh data - if (props.onRefresh !== undefined) { - props.onRefresh(); - } - - // Show alert: success - alerts.addAlert( - "activate-users-success", - "Users activated", - "success" - ); - - closeModal(); - } - } else if (error) { - // Handle error - handleAPIError(error); + if (response.data.result) { + // Close modal + props.handleModalToggle(); + // Update data from Redux + props.selectedUids.map((user) => { + dispatch(removeStageUser(user[0])); + }); + // Set alert: success + alerts.addAlert( + "activate-users-success", + response.data.result.count + " users activated", + "success" + ); + // Refresh data ('Stage users' main page) or redirect ('Settings' page) + props.onSuccess(); + } else if (response.data.error) { + // Set alert: error + const errorMessage = response.data.error as ErrorResult; + alerts.addAlert( + "activate-users-error", + errorMessage.message, + "danger" + ); } } }); @@ -217,34 +131,21 @@ const ActivateStageUsers = (props: PropsToActivateUsers) => { , ]; - const modalActivate: JSX.Element = ( - - ); - // Render 'ActivateStageUsers' return ( <> - {modalActivate} - {isModalErrorOpen && ( - - )} + ); }; diff --git a/src/components/tables/UsersDisplayTable.tsx b/src/components/tables/UsersDisplayTable.tsx index 51d3ae07..18069438 100644 --- a/src/components/tables/UsersDisplayTable.tsx +++ b/src/components/tables/UsersDisplayTable.tsx @@ -53,7 +53,7 @@ const UsersDisplayTable = (props: PropsToDisplayUsersTable) => { case "stage-users": stageUsersListCopy.map((user) => { props.usersToDisplay.map((selected) => { - if (user.uid === selected || user.uid[0] === selected) { + if (user.uid[0] === selected[0] || user.uid[0] === selected) { usersToDisplay.push(user); } }); @@ -63,7 +63,7 @@ const UsersDisplayTable = (props: PropsToDisplayUsersTable) => { case "preserved-users": preservedUsersListCopy.map((user) => { props.usersToDisplay.map((selected) => { - if (user.uid === selected || user.uid[0] === selected) { + if (user.uid[0] === selected[0] || user.uid[0] === selected) { usersToDisplay.push(user); } }); diff --git a/src/pages/StageUsers/StageUsers.tsx b/src/pages/StageUsers/StageUsers.tsx index a32a5ae8..9b9513cf 100644 --- a/src/pages/StageUsers/StageUsers.tsx +++ b/src/pages/StageUsers/StageUsers.tsx @@ -508,8 +508,8 @@ const StageUsers = () => { uid[0])} + onSuccess={refreshUsersData} /> ); diff --git a/src/services/rpc.ts b/src/services/rpc.ts index 0b6db37e..fee32aa7 100644 --- a/src/services/rpc.ts +++ b/src/services/rpc.ts @@ -770,6 +770,22 @@ export const api = createApi({ }, invalidatesTags: ["FullUser"], }), + activateUser: build.mutation({ + query: (query_args) => { + const batchPayload: Command[] = []; + query_args.map((uid) => { + batchPayload.push({ + method: "stageuser_activate", + params: [[uid], {}], + }); + }); + + return getBatchCommand( + batchPayload, + query_args["version"] || API_VERSION_BACKUP + ); + }, + }), }), }); @@ -842,4 +858,5 @@ export const { useGetCertProfileQuery, useAddOtpTokenMutation, useGenerateSubIdsMutation, + useActivateUserMutation, } = api;