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:
jamesgeorge007
2024-03-27 17:42:20 +05:30
parent 8a8cdcf78b
commit dee7864a08
3 changed files with 83 additions and 87 deletions

View File

@@ -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);
}; };

View File

@@ -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;

View File

@@ -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);