Skip to content

Commit

Permalink
Add CL to preserved users page
Browse files Browse the repository at this point in the history
Signed-off-by: Mark Reynolds <[email protected]>
  • Loading branch information
mreynolds389 committed Nov 22, 2023
1 parent a6e978d commit 6dddb12
Show file tree
Hide file tree
Showing 9 changed files with 720 additions and 84 deletions.
36 changes: 17 additions & 19 deletions src/components/modals/AddUser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,24 +48,10 @@ interface GroupId {
export interface PropsToAddUser {
show: boolean;
from: "active-users" | "stage-users" | "preserved-users";
// NOTE: 'setShowTableRows' is handled as { (boolean) => void | undefined } as a temporal solution
// until the C.L. is adapted in 'stage-' and 'preserved users' (otherwise
// the operation will fail for those components)
setShowTableRows?: (value: boolean) => void;
// NOTE: This prop is not needed but it is still being used by 'stage-' and 'preserved users'
// and will be removed when the communication layer is implemented in the buttons od those pages.
handleModalToggle: () => void;
// NOTE: 'onOpenAddModal' is handled as { () => void | undefined } as a temporal solution
// until the C.L. is adapted in 'stage-' and 'preserved users' (otherwise
// the operation will fail for those components)
onOpenAddModal?: () => void;
// NOTE: 'onCloseAddModal' is handled as { () => void | undefined } as a temporal solution
// until the C.L. is adapted in 'stage-' and 'preserved users' (otherwise
// the operation will fail for those components)
onCloseAddModal?: () => void;
// NOTE: 'onRefresh' is handled as { (User) => void | undefined } as a temporal solution
// until the C.L. is adapted in 'stage-' and 'preserved users' (otherwise
// the operation will fail for those components)
onRefresh?: () => void;
}

Expand Down Expand Up @@ -95,6 +81,9 @@ const AddUser = (props: PropsToAddUser) => {
React.useState<boolean>(false);
const [newPassword, setNewPassword] = React.useState("");
const [verifyNewPassword, setVerifyNewPassword] = React.useState("");
const [addSpinning, setAddBtnSpinning] = React.useState<boolean>(false);
const [addAgainSpinning, setAddAgainBtnSpinning] =
React.useState<boolean>(false);

// Verify the passwords are the same when we update a password value
useEffect(() => {
Expand Down Expand Up @@ -588,7 +577,6 @@ const AddUser = (props: PropsToAddUser) => {
const addUserData = async () => {
// If 'userLogin' is not provided, use empty array
const usLogin = userLogin !== "" ? [userLogin] : [];

const newUserData = {
givenname: firstName,
sn: lastName,
Expand Down Expand Up @@ -642,13 +630,16 @@ const AddUser = (props: PropsToAddUser) => {
handleAPIError(error);
}
}
setAddBtnSpinning(false);
setAddAgainBtnSpinning(false);
});
};

const onAddUser = () => {
onAddUserClicked = true;
const validation = validateFields();
if (validation) {
setAddBtnSpinning(true);
addUserData().then(() => {
if (!isAdditionSuccess) {
// Close the modal without cleaning fields
Expand Down Expand Up @@ -688,6 +679,7 @@ const AddUser = (props: PropsToAddUser) => {
onAddUserClicked = false;
const validation = validateFields();
if (validation) {
setAddAgainBtnSpinning(true);
addUserData().then(() => {
if (isAdditionSuccess) {
// Do not close the modal, but clean fields & reset validations
Expand Down Expand Up @@ -763,19 +755,25 @@ const AddUser = (props: PropsToAddUser) => {
<Button
key="add-new-user"
variant="secondary"
isDisabled={buttonDisabled}
isDisabled={buttonDisabled || addAgainSpinning || addSpinning}
onClick={onAddUser}
form="modal-form"
spinnerAriaValueText="Adding"
spinnerAriaLabel="Adding"
isLoading={addSpinning}
>
Add
{addSpinning ? "Adding" : "Add"}
</Button>,
<Button
key="add-and-add-another-user"
variant="secondary"
isDisabled={buttonDisabled}
isDisabled={buttonDisabled || addAgainSpinning || addSpinning}
onClick={onAddAndAddAnother}
spinnerAriaValueText="Adding again"
spinnerAriaLabel="Adding again"
isLoading={addAgainSpinning}
>
Add and add another
{addAgainSpinning ? "Adding" : "Add and add another"}
</Button>,
<Button key="cancel-new-user" variant="link" onClick={cleanAndCloseModal}>
Cancel
Expand Down
18 changes: 16 additions & 2 deletions src/components/modals/DeleteUsers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -193,10 +193,15 @@ const DeleteUsers = (props: PropsToDeleteUsers) => {
setIsModalErrorOpen(true);
};

const [spinning, setBtnSpinning] = React.useState<boolean>(false);

// Delete user
const deleteUsers = (uidsToDelete: string[]) => {
// Prepare users params
const uidsToDeletePayload: Command[] = [];

setBtnSpinning(true);

let deletionParams = {};
if (props.from !== "stage-users") {
deletionParams = { preserve: !isDeleteChecked };
Expand Down Expand Up @@ -278,6 +283,7 @@ const DeleteUsers = (props: PropsToDeleteUsers) => {
handleAPIError(error);
}
}
setBtnSpinning(false);
});
};

Expand All @@ -290,8 +296,12 @@ const DeleteUsers = (props: PropsToDeleteUsers) => {
deleteUsers(props.selectedUsersData.selectedUsers);
}}
form="active-users-remove-users-modal"
spinnerAriaValueText="Deleting"
spinnerAriaLabel="Deleting"
isLoading={spinning}
isDisabled={spinning}
>
Delete
{spinning ? "Deleting" : "Delete"}
</Button>,
<Button key="cancel-new-user" variant="link" onClick={closeModal}>
Cancel
Expand Down Expand Up @@ -332,8 +342,12 @@ const DeleteUsers = (props: PropsToDeleteUsers) => {
deleteUsers(props.selectedUsersData.selectedUsers);
}}
form="active-users-remove-users-modal"
spinnerAriaValueText="Preserving"
spinnerAriaLabel="Preserving"
isLoading={spinning}
isDisabled={spinning}
>
Preserve
{spinning ? "Preserving" : "Preserve"}
</Button>,
<Button key="cancel-new-user" variant="link" onClick={closeModal}>
Cancel
Expand Down
241 changes: 241 additions & 0 deletions src/components/modals/RestorePreservedUsers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
import React, { useState } from "react";
// PatternFly
import {
Button,
Text,
TextContent,
TextVariants,
} from "@patternfly/react-core";
// Layouts
import ModalWithFormLayout from "src/components/layouts/ModalWithFormLayout";
// Tables
import UsersDisplayTable from "src/components/tables/UsersDisplayTable";
// Redux
import { useAppDispatch } from "src/store/hooks";
import { removeUser as removePreservedUser } from "src/store/Identity/preservedUsers-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";
// Hooks
import useAlerts from "src/hooks/useAlerts";

interface SelectedUsersData {
selectedUsers: string[];
updateSelectedUsers: (newSelectedUsers: string[]) => void;
}

export interface PropsToPreservedUsers {
show: boolean;
handleModalToggle: () => void;
selectedUsersData: SelectedUsersData;
onRefresh?: () => void;
}

const RestorePreservedUsers = (props: PropsToPreservedUsers) => {
// Set dispatch (Redux)
const dispatch = useAppDispatch();

// Alerts
const alerts = useAlerts();

// Define 'executeUserRestoreCommand' to restore a preserved user
const [executeUserRestoreCommand] = useBatchMutCommandMutation();

// List of fields
const fields = [
{
id: "question-text",
pfComponent: (
<TextContent>
<Text component={TextVariants.p}>
Are you sure you want to restore the preserved entries?
</Text>
</TextContent>
),
},
{
id: "restore-users-table",
pfComponent: (
<UsersDisplayTable
usersToDisplay={props.selectedUsersData.selectedUsers}
from={"preserved-users"}
/>
),
},
];

// Close modal
const closeModal = () => {
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);
};

const [spinning, setBtnSpinning] = React.useState<boolean>(false);

// Restore preserved user
const restoreUsers = () => {
// Prepare user params
const uidsToRestorePayload: Command[] = [];

setBtnSpinning(true);

props.selectedUsersData.selectedUsers.map((uid) => {
const payloadItem = {
method: "user_undel",
params: [uid, {}],
} as Command;
uidsToRestorePayload.push(payloadItem);
});

// [API call] Restore elements
executeUserRestoreCommand(uidsToRestorePayload).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(removePreservedUser(user[0]));
});

// Reset selected values
props.selectedUsersData.updateSelectedUsers([]);

// Refresh data
if (props.onRefresh !== undefined) {
props.onRefresh();
}

// Show alert: success
alerts.addAlert(
"restore-users-success",
"Users restored",
"success"
);

closeModal();
}
} else if (error) {
// Handle error
handleAPIError(error);
}
}
setBtnSpinning(false);
});
};

// Set the Modal and Action buttons for 'Restore' option
const modalRestoreActions: JSX.Element[] = [
<Button
key="restore-users"
variant="primary"
onClick={restoreUsers}
form="restore-users-modal"
spinnerAriaValueText="Restoring"
spinnerAriaLabel="Restoring"
isLoading={spinning}
isDisabled={spinning}
>
{spinning ? "Restoring" : "Restore"}
</Button>,
<Button key="cancel-restore-user" variant="link" onClick={closeModal}>
Cancel
</Button>,
];

const modalRestore: JSX.Element = (
<ModalWithFormLayout
variantType="medium"
modalPosition="top"
offPosition="76px"
title="Restore preserved user"
formId="restore-users-stage-modal"
fields={fields}
show={props.show}
onClose={closeModal}
actions={modalRestoreActions}
/>
);

return (
<>
<alerts.ManagedAlerts />
{modalRestore}
{isModalErrorOpen && (
<ErrorModal
title={errorTitle}
isOpen={isModalErrorOpen}
onClose={onCloseErrorModal}
actions={errorModalActions}
errorMessage={errorMessage}
/>
)}
</>
);
};

export default RestorePreservedUsers;
Loading

0 comments on commit 6dddb12

Please sign in to comment.