chore: address CR comments
- Implicitly infer the action type (bulk/individual) from the supplied deleted users list. - Display toast messages one after the other by relying on the native toast APIs refraining from the need to maintain timeouts separately. - Ensure the toast message about user deletion success/failure with the count is displayed only when above `0`. - Cleanup. Co-authored-by: amk-dev <akash.k.mohan98@gmail.com> Co-authored-by: nivedin <nivedinp@gmail.com>
This commit is contained in:
@@ -3,106 +3,112 @@ import { getI18n } from '~/modules/i18n';
|
|||||||
import { UserDeletionResult } from './backend/graphql';
|
import { UserDeletionResult } from './backend/graphql';
|
||||||
import { ADMIN_CANNOT_BE_DELETED, USER_IS_OWNER } from './errors';
|
import { ADMIN_CANNOT_BE_DELETED, USER_IS_OWNER } from './errors';
|
||||||
|
|
||||||
type IndividualActionInput = {
|
type IndividualActionMetadata = null;
|
||||||
type: 'individual';
|
type BulkActionMetadata = {
|
||||||
metadata: null;
|
areMultipleUsersSelected: boolean;
|
||||||
};
|
deletedUserIDs: string[];
|
||||||
type BulkActionInput = {
|
|
||||||
type: 'bulk';
|
|
||||||
metadata: {
|
|
||||||
areMultipleUsersSelected: boolean;
|
|
||||||
deletedIDs: string[];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
type IndividualActionResult = {
|
|
||||||
data: null;
|
|
||||||
};
|
|
||||||
type BulkActionResult = {
|
|
||||||
data: { timeoutID: NodeJS.Timeout | null };
|
|
||||||
};
|
};
|
||||||
|
type ActionMetadata = IndividualActionMetadata | BulkActionMetadata;
|
||||||
|
|
||||||
type HandleUserDeletion = {
|
type HandleUserDeletion = {
|
||||||
(
|
(deletedUsersList: UserDeletionResult[], metadata: ActionMetadata): void;
|
||||||
deletedUsersList: UserDeletionResult[],
|
};
|
||||||
action: IndividualActionInput | BulkActionInput
|
|
||||||
): IndividualActionResult | BulkActionResult;
|
type ToastMessage = {
|
||||||
|
message: string;
|
||||||
|
state: 'success' | 'error';
|
||||||
};
|
};
|
||||||
|
|
||||||
const t = getI18n();
|
const t = getI18n();
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
|
||||||
|
const displayToastMessages = (
|
||||||
|
toastMessages: ToastMessage[],
|
||||||
|
currentIndex: number
|
||||||
|
) => {
|
||||||
|
const { message, state } = toastMessages[currentIndex];
|
||||||
|
|
||||||
|
toast[state](message, {
|
||||||
|
duration: 2000,
|
||||||
|
onComplete: () => {
|
||||||
|
if (currentIndex < toastMessages.length - 1) {
|
||||||
|
displayToastMessages(toastMessages, currentIndex + 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const handleUserDeletion: HandleUserDeletion = (
|
export const handleUserDeletion: HandleUserDeletion = (
|
||||||
deletedUsersList,
|
deletedUsersList,
|
||||||
action
|
metadata
|
||||||
) => {
|
) => {
|
||||||
let timeoutID: NodeJS.Timeout | null = null;
|
|
||||||
|
|
||||||
const uniqueErrorMessages = new Set(
|
const uniqueErrorMessages = new Set(
|
||||||
deletedUsersList.map(({ errorMessage }) => errorMessage).filter(Boolean)
|
deletedUsersList.map(({ errorMessage }) => errorMessage).filter(Boolean)
|
||||||
) as Set<string>;
|
) as Set<string>;
|
||||||
|
|
||||||
const { type, metadata } = action;
|
const isBulkAction = deletedUsersList.length > 1;
|
||||||
|
|
||||||
// Show the success toast based on the action type if there are no errors
|
// Show the success toast based on the action type if there are no errors
|
||||||
if (uniqueErrorMessages.size === 0) {
|
if (uniqueErrorMessages.size === 0) {
|
||||||
if (type === 'bulk') {
|
if (isBulkAction) {
|
||||||
toast.success(
|
toast.success(
|
||||||
metadata.areMultipleUsersSelected
|
(metadata as BulkActionMetadata).areMultipleUsersSelected
|
||||||
? t('state.delete_user_success')
|
? t('state.delete_user_success')
|
||||||
: t('state.delete_users_success')
|
: t('state.delete_users_success')
|
||||||
);
|
);
|
||||||
|
|
||||||
return { type, data: { timeoutID } };
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
toast.success(t('state.delete_user_success'));
|
toast.success(t('state.delete_user_success'));
|
||||||
return { type, data: null };
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const errMsgMap = {
|
const errMsgMap = {
|
||||||
[ADMIN_CANNOT_BE_DELETED]:
|
[ADMIN_CANNOT_BE_DELETED]: isBulkAction
|
||||||
type === 'bulk'
|
? t('state.remove_admin_for_deletion')
|
||||||
? t('state.remove_admin_for_deletion')
|
: t('state.remove_admin_to_delete_user'),
|
||||||
: t('state.remove_admin_to_delete_user'),
|
|
||||||
|
|
||||||
[USER_IS_OWNER]:
|
[USER_IS_OWNER]: isBulkAction
|
||||||
type === 'bulk'
|
? t('state.remove_owner_for_deletion')
|
||||||
? t('state.remove_owner_for_deletion')
|
: t('state.remove_owner_to_delete_user'),
|
||||||
: t('state.remove_owner_to_delete_user'),
|
|
||||||
};
|
};
|
||||||
const errMsgMapKeys = Object.keys(errMsgMap);
|
const errMsgMapKeys = Object.keys(errMsgMap);
|
||||||
|
|
||||||
if (type === 'bulk') {
|
const toastMessages: ToastMessage[] = [];
|
||||||
const { areMultipleUsersSelected, deletedIDs } = metadata;
|
|
||||||
|
|
||||||
// Show toast messages with the count of users deleted only if multiple users are selected
|
if (isBulkAction) {
|
||||||
if (areMultipleUsersSelected) {
|
const { areMultipleUsersSelected, deletedUserIDs } =
|
||||||
toast.success(
|
metadata as BulkActionMetadata;
|
||||||
t('state.delete_some_users_success', { count: deletedIDs.length })
|
|
||||||
);
|
// Indicates the actual count of users deleted (filtered via the `isDeleted` field)
|
||||||
toast.error(
|
const deletedUsersCount = deletedUserIDs.length;
|
||||||
t('state.delete_some_users_failure', {
|
|
||||||
count: deletedUsersList.length - deletedIDs.length,
|
if (areMultipleUsersSelected && deletedUsersCount > 0) {
|
||||||
})
|
toastMessages.push({
|
||||||
);
|
message: t('state.delete_some_users_success', {
|
||||||
|
count: deletedUsersCount,
|
||||||
|
}),
|
||||||
|
state: 'success',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const remainingDeletionsCount = deletedUsersList.length - deletedUsersCount;
|
||||||
|
if (remainingDeletionsCount > 0) {
|
||||||
|
toastMessages.push({
|
||||||
|
message: t('state.delete_some_users_failure', {
|
||||||
|
count: remainingDeletionsCount,
|
||||||
|
}),
|
||||||
|
state: 'error',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uniqueErrorMessages.forEach((errorMessage) => {
|
uniqueErrorMessages.forEach((errorMessage) => {
|
||||||
if (errMsgMapKeys.includes(errorMessage)) {
|
if (errMsgMapKeys.includes(errorMessage)) {
|
||||||
if (type === 'bulk') {
|
toastMessages.push({
|
||||||
timeoutID = setTimeout(
|
message: errMsgMap[errorMessage as keyof typeof errMsgMap],
|
||||||
() => {
|
state: 'error',
|
||||||
toast.error(errMsgMap[errorMessage as keyof typeof errMsgMap]);
|
});
|
||||||
},
|
|
||||||
metadata.areMultipleUsersSelected ? 2000 : 0
|
|
||||||
);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
toast.error(errMsgMap[errorMessage as keyof typeof errMsgMap]);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -112,10 +118,16 @@ export const handleUserDeletion: HandleUserDeletion = (
|
|||||||
(key) => !((key as string) in errMsgMap)
|
(key) => !((key as string) in errMsgMap)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
type === 'bulk' && metadata.areMultipleUsersSelected
|
const fallbackErrMsg =
|
||||||
? t('state.delete_users_failure')
|
isBulkAction && (metadata as BulkActionMetadata).areMultipleUsersSelected
|
||||||
: t('state.delete_user_failure');
|
? t('state.delete_users_failure')
|
||||||
|
: t('state.delete_user_failure');
|
||||||
|
|
||||||
|
toastMessages.push({
|
||||||
|
message: fallbackErrMsg,
|
||||||
|
state: 'error',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return { data: type === 'bulk' ? { timeoutID } : null };
|
displayToastMessages(toastMessages, 0);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -210,10 +210,7 @@ const deleteUserMutation = async (id: string | null) => {
|
|||||||
} else {
|
} else {
|
||||||
const deletedUsers = result.data?.removeUsersByAdmin || [];
|
const deletedUsers = result.data?.removeUsersByAdmin || [];
|
||||||
|
|
||||||
handleUserDeletion(deletedUsers, {
|
handleUserDeletion(deletedUsers, null);
|
||||||
type: 'individual',
|
|
||||||
metadata: null,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
confirmDeletion.value = false;
|
confirmDeletion.value = false;
|
||||||
deleteUserUID.value = null;
|
deleteUserUID.value = null;
|
||||||
|
|||||||
@@ -309,16 +309,10 @@ const selectedRows = ref<UsersListQuery['infra']['allUsers']>([]);
|
|||||||
// Ensure this variable is declared outside the debounce function
|
// Ensure this variable is declared outside the debounce function
|
||||||
let debounceTimeout: ReturnType<typeof setTimeout> | null = null;
|
let debounceTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
|
||||||
let toastTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
if (debounceTimeout) {
|
if (debounceTimeout) {
|
||||||
clearTimeout(debounceTimeout);
|
clearTimeout(debounceTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toastTimeout) {
|
|
||||||
clearTimeout(toastTimeout);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Debounce Function
|
// Debounce Function
|
||||||
@@ -586,24 +580,17 @@ const deleteUsers = async (id: string | null) => {
|
|||||||
toast.error(errorMessage);
|
toast.error(errorMessage);
|
||||||
} else {
|
} else {
|
||||||
const deletedUsers = result.data?.removeUsersByAdmin || [];
|
const deletedUsers = result.data?.removeUsersByAdmin || [];
|
||||||
const deletedIDs = deletedUsers
|
const deletedUserIDs = deletedUsers
|
||||||
.filter((user) => user.isDeleted)
|
.filter((user) => user.isDeleted)
|
||||||
.map((user) => user.userUID);
|
.map((user) => user.userUID);
|
||||||
|
|
||||||
const { data } = handleUserDeletion(deletedUsers, {
|
handleUserDeletion(deletedUsers, {
|
||||||
type: 'bulk',
|
areMultipleUsersSelected: areMultipleUsersSelected.value,
|
||||||
metadata: {
|
deletedUserIDs,
|
||||||
areMultipleUsersSelected: areMultipleUsersSelected.value,
|
|
||||||
deletedIDs,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (data?.timeoutID) {
|
|
||||||
toastTimeout = data.timeoutID;
|
|
||||||
}
|
|
||||||
|
|
||||||
usersList.value = usersList.value.filter(
|
usersList.value = usersList.value.filter(
|
||||||
(user) => !deletedIDs.includes(user.uid)
|
(user) => !deletedUserIDs.includes(user.uid)
|
||||||
);
|
);
|
||||||
|
|
||||||
selectedRows.value.splice(0, selectedRows.value.length);
|
selectedRows.value.splice(0, selectedRows.value.length);
|
||||||
|
|||||||
Reference in New Issue
Block a user