Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Stage users][Settings][Kebab] Add 'Activate' option ('Stage users') #225

Merged
merged 1 commit into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion src/components/UserSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<User>;
Expand All @@ -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") {
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -295,7 +308,9 @@ const UserSettings = (props: PropsToUserSettings) => {
];

const stageDropdownItems = [
<DropdownItem key="activate">Activate</DropdownItem>,
<DropdownItem key="activate" onClick={() => setIsActivateModalOpen(true)}>
Activate
</DropdownItem>,
<DropdownItem key="delete" onClick={() => setIsDeleteModalOpen(true)}>
Delete
</DropdownItem>,
Expand Down Expand Up @@ -603,6 +618,12 @@ const UserSettings = (props: PropsToUserSettings) => {
onClose={onCloseAddOtpTokenModal}
/>
)}
<ActivateStageUsers
show={isActivateModalOpen}
handleModalToggle={onCloseActivateModal}
selectedUids={selectedUsers}
onSuccess={() => navigate(URL_PREFIX + "/stage-users")}
/>
</>
);
};
Expand Down
181 changes: 41 additions & 140 deletions src/components/modals/ActivateStageUsers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand All @@ -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<boolean>(false);

Expand All @@ -71,7 +54,7 @@ const ActivateStageUsers = (props: PropsToActivateUsers) => {
id: "activate-users-table",
pfComponent: (
<UsersDisplayTable
usersToDisplay={props.selectedUsersData.selectedUsers}
usersToDisplay={props.selectedUids}
from={"stage-users"}
/>
),
Expand All @@ -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 = [
<Button key="cancel" variant="link" onClick={onCloseErrorModal}>
OK
</Button>,
];

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"
);
}
}
});
Expand All @@ -217,34 +131,21 @@ const ActivateStageUsers = (props: PropsToActivateUsers) => {
</Button>,
];

const modalActivate: JSX.Element = (
<ModalWithFormLayout
variantType="medium"
modalPosition="top"
offPosition="76px"
title="Activate Stage User"
formId="stage-user-activate-modal"
fields={fields}
show={props.show}
onClose={closeModal}
actions={modalStageActions}
/>
);

// Render 'ActivateStageUsers'
return (
<>
<alerts.ManagedAlerts />
{modalActivate}
{isModalErrorOpen && (
<ErrorModal
title={errorTitle}
isOpen={isModalErrorOpen}
onClose={onCloseErrorModal}
actions={errorModalActions}
errorMessage={errorMessage}
/>
)}
<ModalWithFormLayout
variantType="medium"
modalPosition="top"
offPosition="76px"
title="Activate Stage User"
formId="stage-user-activate-modal"
fields={fields}
show={props.show}
onClose={closeModal}
actions={modalStageActions}
/>
</>
);
};
Expand Down
4 changes: 2 additions & 2 deletions src/components/tables/UsersDisplayTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
});
Expand All @@ -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);
}
});
Expand Down
4 changes: 2 additions & 2 deletions src/pages/StageUsers/StageUsers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -508,8 +508,8 @@ const StageUsers = () => {
<ActivateStageUsers
show={showActivateModal}
handleModalToggle={onActivateModalToggle}
selectedUsersData={selectedUsersData}
onRefresh={refreshUsersData}
selectedUids={selectedUsers.map((uid) => uid[0])}
onSuccess={refreshUsersData}
/>
</Page>
);
Expand Down
17 changes: 17 additions & 0 deletions src/services/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,22 @@ export const api = createApi({
},
invalidatesTags: ["FullUser"],
}),
activateUser: build.mutation<FindRPCResponse, string[]>({
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
);
},
}),
}),
});

Expand Down Expand Up @@ -842,4 +858,5 @@ export const {
useGetCertProfileQuery,
useAddOtpTokenMutation,
useGenerateSubIdsMutation,
useActivateUserMutation,
} = api;
Loading