From 7ccda5dd2bcdec2d4e39851b8a42d972e0178182 Mon Sep 17 00:00:00 2001 From: Iveta Date: Wed, 3 Jan 2024 15:40:32 -0500 Subject: [PATCH] Standardize error with extras (#61) --- src/api/handleApiErrorString.ts | 15 --------- src/apiQueries/useResetPassword.ts | 11 +++++-- src/components/DashboardAnalytics.tsx | 3 +- src/components/DisbursementsTable.tsx | 9 ++++-- src/components/ErrorWithExtras.tsx | 16 ++++++++++ src/components/NewUserModal.tsx | 7 ++++- src/components/PaymentsTable.tsx | 9 ++++-- src/components/QueryStatusHandler.tsx | 7 ++++- .../ReceiverInviteMessage/index.tsx | 3 +- src/components/ReceiverWalletBalance.tsx | 3 +- src/components/ReceiverWalletHistory.tsx | 3 +- src/components/ReceiversTable.tsx | 9 ++++-- .../SettingsEnablePaymentCancellation.tsx | 3 +- src/components/SettingsEnableSmsRetry.tsx | 3 +- src/components/SettingsTeamMembers.tsx | 7 +++-- src/components/SettingsTimezone.tsx | 8 ++++- src/components/WalletTrustlines/index.tsx | 3 +- src/helpers/fetchApi.ts | 2 +- src/helpers/normalizeApiError.ts | 14 +++++++-- src/pages/DisbursementDetails.tsx | 12 +++++-- src/pages/DisbursementDraftDetails.tsx | 18 +++++------ src/pages/Disbursements.tsx | 4 ++- src/pages/DisbursementsDrafts.tsx | 7 ++++- src/pages/DisbursementsNew.tsx | 17 +++++----- src/pages/DistributionAccount.tsx | 8 ++++- src/pages/ForgotPassword.tsx | 3 +- src/pages/Home.tsx | 4 ++- src/pages/MFAuth.tsx | 7 ++++- src/pages/PaymentDetails.tsx | 8 ++++- src/pages/Profile.tsx | 31 ++++++++----------- src/pages/ReceiverDetails.tsx | 9 ++++-- src/pages/ReceiverDetailsEdit.tsx | 9 ++++-- src/pages/ResetPassword.tsx | 10 ++---- src/pages/SetNewPassword.tsx | 10 ++---- src/pages/SignIn.tsx | 7 ++++- src/pages/WalletProviders.tsx | 3 +- src/store/ducks/disbursementDetails.ts | 11 ++++--- src/store/ducks/disbursementDrafts.ts | 28 ++++++++--------- src/store/ducks/disbursements.ts | 8 +++-- src/store/ducks/organization.ts | 17 +++++----- src/store/ducks/profile.ts | 11 ++++--- src/store/ducks/userAccount.ts | 11 ++++--- 42 files changed, 242 insertions(+), 146 deletions(-) delete mode 100644 src/api/handleApiErrorString.ts create mode 100644 src/components/ErrorWithExtras.tsx diff --git a/src/api/handleApiErrorString.ts b/src/api/handleApiErrorString.ts deleted file mode 100644 index 2a8e50d..0000000 --- a/src/api/handleApiErrorString.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { GENERIC_ERROR_MESSAGE } from "constants/settings"; -import { ApiError } from "types"; - -export const handleApiErrorString = (error: ApiError): string => { - // Make sure error is not an empty object - if (JSON.stringify(error) === "{}") { - return GENERIC_ERROR_MESSAGE; - } - - return (error?.extras?.details || - error?.extras?.message || - error.error || - error || - GENERIC_ERROR_MESSAGE) as string; -}; diff --git a/src/apiQueries/useResetPassword.ts b/src/apiQueries/useResetPassword.ts index 9fc418b..68953de 100644 --- a/src/apiQueries/useResetPassword.ts +++ b/src/apiQueries/useResetPassword.ts @@ -1,6 +1,7 @@ import { useMutation } from "@tanstack/react-query"; import { API_URL } from "constants/settings"; import { fetchApi } from "helpers/fetchApi"; +import { normalizeApiError } from "helpers/normalizeApiError"; import { AppError } from "types"; type ResetPasswordProps = { @@ -22,12 +23,18 @@ export const useResetPassword = () => { }, { withoutAuth: true, - customCallback: (response) => { + customCallback: async (response) => { if (response.status === 200) { return true; } - return response; + const responseJson = await response.json(); + + if (responseJson?.error) { + throw normalizeApiError(responseJson); + } + + return responseJson; }, }, ); diff --git a/src/components/DashboardAnalytics.tsx b/src/components/DashboardAnalytics.tsx index 45ea1e5..6275670 100644 --- a/src/components/DashboardAnalytics.tsx +++ b/src/components/DashboardAnalytics.tsx @@ -1,6 +1,7 @@ import { Card, Notification } from "@stellar/design-system"; import { InfoTooltip } from "components/InfoTooltip"; import { AssetAmount } from "components/AssetAmount"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; import { useStatistics } from "apiQueries/useStatistics"; import { percent } from "helpers/formatIntlNumber"; @@ -28,7 +29,7 @@ export const DashboardAnalytics = () => { if (error) { return ( - {error.message} + ); } diff --git a/src/components/DisbursementsTable.tsx b/src/components/DisbursementsTable.tsx index 196d8b5..6f6423e 100644 --- a/src/components/DisbursementsTable.tsx +++ b/src/components/DisbursementsTable.tsx @@ -6,6 +6,7 @@ import { renderNumberOrDash } from "helpers/renderNumberOrDash"; import { useSort } from "hooks/useSort"; import { Table } from "components/Table"; import { AssetAmount } from "components/AssetAmount"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; import { ActionStatus, Disbursement, @@ -17,7 +18,7 @@ import { interface DisbursementsTableProps { disbursementItems: Disbursement[]; searchParams: DisbursementsSearchParams | undefined; - apiError: string | boolean | undefined; + apiError: string | undefined; isFiltersSelected: boolean | undefined; status: ActionStatus | undefined; onSort?: (sort?: SortByDisbursements, direction?: SortDirection) => void; @@ -52,7 +53,11 @@ export const DisbursementsTable: React.FC = ({ if (apiError) { return ( - {apiError} + ); } diff --git a/src/components/ErrorWithExtras.tsx b/src/components/ErrorWithExtras.tsx new file mode 100644 index 0000000..9f30e44 --- /dev/null +++ b/src/components/ErrorWithExtras.tsx @@ -0,0 +1,16 @@ +import { AppError } from "types"; + +export const ErrorWithExtras = ({ appError }: { appError: AppError }) => { + return ( + <> +
{appError.message}
+ {appError.extras ? ( + + ) : null} + + ); +}; diff --git a/src/components/NewUserModal.tsx b/src/components/NewUserModal.tsx index 4ccb7d6..5fb3177 100644 --- a/src/components/NewUserModal.tsx +++ b/src/components/NewUserModal.tsx @@ -8,6 +8,7 @@ import { Notification, } from "@stellar/design-system"; import { InfoTooltip } from "components/InfoTooltip"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; import { USER_ROLES_ARRAY } from "constants/settings"; import { userRoleText } from "helpers/userRoleText"; import { usePrevious } from "hooks/usePrevious"; @@ -154,7 +155,11 @@ export const NewUserModal: React.FC = ({ {errorMessage ? ( - {errorMessage} + ) : null} diff --git a/src/components/PaymentsTable.tsx b/src/components/PaymentsTable.tsx index 444e0e2..fb6ee4a 100644 --- a/src/components/PaymentsTable.tsx +++ b/src/components/PaymentsTable.tsx @@ -5,12 +5,13 @@ import { Routes } from "constants/settings"; import { AssetAmount } from "components/AssetAmount"; import { PaymentStatus } from "components/PaymentStatus"; import { Table } from "components/Table"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; import { formatDateTime } from "helpers/formatIntlDateTime"; import { ApiPayment } from "types"; interface PaymentsTableProps { paymentItems: ApiPayment[]; - apiError: string | boolean | undefined; + apiError: string | undefined; isFiltersSelected: boolean | undefined; isLoading: boolean; } @@ -42,7 +43,11 @@ export const PaymentsTable = ({ if (apiError) { return ( - {apiError} + ); } diff --git a/src/components/QueryStatusHandler.tsx b/src/components/QueryStatusHandler.tsx index 09e73fc..2bc8f5c 100644 --- a/src/components/QueryStatusHandler.tsx +++ b/src/components/QueryStatusHandler.tsx @@ -1,4 +1,5 @@ import { Notification } from "@stellar/design-system"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; interface QueryStatusHandlerProps { isLoading?: boolean; @@ -24,7 +25,11 @@ export const QueryStatusHandler = ({ if (isError) { return ( - {errorMessage} + ); } diff --git a/src/components/ReceiverInviteMessage/index.tsx b/src/components/ReceiverInviteMessage/index.tsx index 8b04cad..32d3a30 100644 --- a/src/components/ReceiverInviteMessage/index.tsx +++ b/src/components/ReceiverInviteMessage/index.tsx @@ -10,6 +10,7 @@ import { import { useDispatch } from "react-redux"; import { NotificationWithButtons } from "components/NotificationWithButtons"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; import { useUpdateSmsTemplate } from "apiQueries/useUpdateOrgSmsTemplate"; import { useRedux } from "hooks/useRedux"; import { AppDispatch } from "store"; @@ -181,7 +182,7 @@ export const ReceiverInviteMessage = () => { <> {isError ? ( - {error.message} + ) : null} diff --git a/src/components/ReceiverWalletBalance.tsx b/src/components/ReceiverWalletBalance.tsx index 751cf07..13fefe5 100644 --- a/src/components/ReceiverWalletBalance.tsx +++ b/src/components/ReceiverWalletBalance.tsx @@ -2,6 +2,7 @@ import { Fragment } from "react"; import { Loader, Notification } from "@stellar/design-system"; import { useStellarAccountInfo } from "apiQueries/useStellarAccountInfo"; import { AssetAmount } from "components/AssetAmount"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; interface ReceiverWalletBalanceProps { stellarAddress: string | undefined; @@ -23,7 +24,7 @@ export const ReceiverWalletBalance = ({ if (error) { return ( - {error.message} + ); } diff --git a/src/components/ReceiverWalletHistory.tsx b/src/components/ReceiverWalletHistory.tsx index 96487dd..e79409a 100644 --- a/src/components/ReceiverWalletHistory.tsx +++ b/src/components/ReceiverWalletHistory.tsx @@ -5,6 +5,7 @@ import { formatDateTime } from "helpers/formatIntlDateTime"; import { Table } from "components/Table"; import { AssetAmount } from "components/AssetAmount"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; interface ReceiverWalletHistoryProps { stellarAddress: string | undefined; @@ -23,7 +24,7 @@ export const ReceiverWalletHistory = ({ if (error) { return ( - {error.message} + ); } diff --git a/src/components/ReceiversTable.tsx b/src/components/ReceiversTable.tsx index 373c4c2..cf94793 100644 --- a/src/components/ReceiversTable.tsx +++ b/src/components/ReceiversTable.tsx @@ -3,6 +3,7 @@ import { formatDateTime } from "helpers/formatIntlDateTime"; import { useSort } from "hooks/useSort"; import { MultipleAmounts } from "components/MultipleAmounts"; import { Table } from "components/Table"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; import { Receiver, SortByReceivers, SortDirection } from "types"; interface ReceiversTableProps { @@ -12,7 +13,7 @@ interface ReceiversTableProps { id: string, ) => void; searchQuery: string | undefined; - apiError: string | boolean | undefined; + apiError: string | undefined; isFiltersSelected: boolean | undefined; isLoading?: boolean; onSort?: (sort?: SortByReceivers, direction?: SortDirection) => void; @@ -32,7 +33,11 @@ export const ReceiversTable: React.FC = ({ if (apiError) { return ( - {apiError} + ); } diff --git a/src/components/SettingsEnablePaymentCancellation.tsx b/src/components/SettingsEnablePaymentCancellation.tsx index 6cee1e1..aee0917 100644 --- a/src/components/SettingsEnablePaymentCancellation.tsx +++ b/src/components/SettingsEnablePaymentCancellation.tsx @@ -11,6 +11,7 @@ import { useDispatch } from "react-redux"; import { DropdownMenu } from "components/DropdownMenu"; import { MoreMenuButton } from "components/MoreMenuButton"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; import { useUpdateOrgPaymentCancellationPeriodDays } from "apiQueries/useUpdateOrgPaymentCancellationPeriodDays"; import { useRedux } from "hooks/useRedux"; @@ -168,7 +169,7 @@ export const SettingsEnablePaymentCancellation = () => { <> {error ? ( - {error.message} + ) : null} diff --git a/src/components/SettingsEnableSmsRetry.tsx b/src/components/SettingsEnableSmsRetry.tsx index 6cf57ad..2d6d69f 100644 --- a/src/components/SettingsEnableSmsRetry.tsx +++ b/src/components/SettingsEnableSmsRetry.tsx @@ -11,6 +11,7 @@ import { useDispatch } from "react-redux"; import { DropdownMenu } from "components/DropdownMenu"; import { MoreMenuButton } from "components/MoreMenuButton"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; import { useUpdateOrgSmsRetryInterval } from "apiQueries/useUpdateOrgSmsRetryInterval"; import { useRedux } from "hooks/useRedux"; @@ -151,7 +152,7 @@ export const SettingsEnableSmsRetry = () => { <> {error ? ( - {error.message} + ) : null} diff --git a/src/components/SettingsTeamMembers.tsx b/src/components/SettingsTeamMembers.tsx index a7ad051..38ff32b 100644 --- a/src/components/SettingsTeamMembers.tsx +++ b/src/components/SettingsTeamMembers.tsx @@ -14,6 +14,7 @@ import { Table } from "components/Table"; import { NewUserModal } from "components/NewUserModal"; import { LoadingContent } from "components/LoadingContent"; import { NotificationWithButtons } from "components/NotificationWithButtons"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; import { USER_ROLES_ARRAY } from "constants/settings"; import { userRoleText } from "helpers/userRoleText"; @@ -265,9 +266,9 @@ export const SettingsTeamMembers = () => { {usersError || roleError || statusError ? ( - {usersError?.message || - roleError?.message || - statusError?.message} + ) : null} diff --git a/src/components/SettingsTimezone.tsx b/src/components/SettingsTimezone.tsx index 03bb179..8997ea9 100644 --- a/src/components/SettingsTimezone.tsx +++ b/src/components/SettingsTimezone.tsx @@ -5,6 +5,7 @@ import { InfoTooltip } from "components/InfoTooltip"; import { DropdownMenu } from "components/DropdownMenu"; import { MoreMenuButton } from "components/MoreMenuButton"; import { NotificationWithButtons } from "components/NotificationWithButtons"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; import { TIME_ZONES } from "constants/settings"; import { useRedux } from "hooks/useRedux"; import { AppDispatch } from "store"; @@ -73,7 +74,12 @@ export const SettingsTimezone = () => { {organization.errorString ? ( - {organization.errorString} + ) : null} diff --git a/src/components/WalletTrustlines/index.tsx b/src/components/WalletTrustlines/index.tsx index a66b89e..acda318 100644 --- a/src/components/WalletTrustlines/index.tsx +++ b/src/components/WalletTrustlines/index.tsx @@ -11,6 +11,7 @@ import { InfoTooltip } from "components/InfoTooltip"; import { DropdownMenu } from "components/DropdownMenu"; import { MoreMenuButton } from "components/MoreMenuButton"; import { NotificationWithButtons } from "components/NotificationWithButtons"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; import { useBalanceTrustline } from "apiQueries/useBalanceTrustline"; import { useAssetsAdd } from "apiQueries/useAssetsAdd"; @@ -242,7 +243,7 @@ export const WalletTrustlines = ({ <> {trustlinesError ? ( - {trustlinesError.message} + ) : null} diff --git a/src/helpers/fetchApi.ts b/src/helpers/fetchApi.ts index fc4c4a4..6ba048e 100644 --- a/src/helpers/fetchApi.ts +++ b/src/helpers/fetchApi.ts @@ -66,7 +66,7 @@ export const fetchApi = async ( const response = await request.json(); if (response?.error) { - throw normalizeApiError(response.error); + throw normalizeApiError(response); } return response; diff --git a/src/helpers/normalizeApiError.ts b/src/helpers/normalizeApiError.ts index b184082..9214682 100644 --- a/src/helpers/normalizeApiError.ts +++ b/src/helpers/normalizeApiError.ts @@ -6,6 +6,7 @@ export const normalizeApiError = ( defaultMessage = GENERIC_ERROR_MESSAGE, ): AppError => { let message = ""; + const extras = error?.extras; // Make sure error is not an empty object if (JSON.stringify(error) === "{}") { @@ -18,8 +19,17 @@ export const normalizeApiError = ( defaultMessage) as string; } + // Remove details and message from extras to avoid duplicate messages + if (extras?.details) { + delete extras.details; + } + + if (extras?.message) { + delete extras.message; + } + return { - message: message, - extras: error?.extras, + message, + extras, }; }; diff --git a/src/pages/DisbursementDetails.tsx b/src/pages/DisbursementDetails.tsx index 451c081..0610b49 100644 --- a/src/pages/DisbursementDetails.tsx +++ b/src/pages/DisbursementDetails.tsx @@ -20,6 +20,7 @@ import { setDisbursementDetailsAction, } from "store/ducks/disbursementDetails"; import { useRedux } from "hooks/useRedux"; +import { useDownloadCsvFile } from "hooks/useDownloadCsvFile"; import { PAGE_LIMIT_OPTIONS, Routes, @@ -33,11 +34,12 @@ import { formatDateTime } from "helpers/formatIntlDateTime"; import { AssetAmount } from "components/AssetAmount"; import { Pagination } from "components/Pagination"; import { Table } from "components/Table"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; +import { PaymentStatus } from "components/PaymentStatus"; + import { renderNumberOrDash } from "helpers/renderNumberOrDash"; import { number } from "helpers/formatIntlNumber"; -import { PaymentStatus } from "components/PaymentStatus"; import { saveFile } from "helpers/saveFile"; -import { useDownloadCsvFile } from "hooks/useDownloadCsvFile"; export const DisbursementDetails = () => { const { id: disbursementId } = useParams(); @@ -388,7 +390,11 @@ export const DisbursementDetails = () => { if (disbursementDetails.errorString) { return ( - {disbursementDetails.errorString} + ); } diff --git a/src/pages/DisbursementDraftDetails.tsx b/src/pages/DisbursementDraftDetails.tsx index 4ed756b..aec0feb 100644 --- a/src/pages/DisbursementDraftDetails.tsx +++ b/src/pages/DisbursementDraftDetails.tsx @@ -26,6 +26,8 @@ import { Toast } from "components/Toast"; import { DisbursementDetails } from "components/DisbursementDetails"; import { DisbursementInstructions } from "components/DisbursementInstructions"; import { DisbursementButtons } from "components/DisbursementButtons"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; + import { DisbursementDraft, DisbursementStep } from "types"; export const DisbursementDraftDetails = () => { @@ -379,16 +381,12 @@ export const DisbursementDraftDetails = () => { : "Error" } > -
{apiError}
- {disbursementDrafts.errorExtras ? ( -
    - {Object.entries(disbursementDrafts.errorExtras).map( - ([key, value]) => ( -
  • {`${key}: ${value}`}
  • - ), - )} -
- ) : null} + ) : null} diff --git a/src/pages/Disbursements.tsx b/src/pages/Disbursements.tsx index 8079e2a..db35afc 100644 --- a/src/pages/Disbursements.tsx +++ b/src/pages/Disbursements.tsx @@ -60,7 +60,9 @@ export const Disbursements = () => { }, [dispatch]); const apiError = - disbursements.status === "ERROR" && disbursements.errorString; + disbursements.status === "ERROR" && disbursements.errorString + ? disbursements.errorString + : undefined; const maxPages = disbursements.pagination?.pages || 1; const isSearchInProgress = Boolean( disbursements.searchParams?.q && disbursements.status === "PENDING", diff --git a/src/pages/DisbursementsDrafts.tsx b/src/pages/DisbursementsDrafts.tsx index ff5462e..57d4893 100644 --- a/src/pages/DisbursementsDrafts.tsx +++ b/src/pages/DisbursementsDrafts.tsx @@ -23,6 +23,7 @@ import { Breadcrumbs } from "components/Breadcrumbs"; import { NewDisbursementButton } from "components/NewDisbursementButton"; import { SectionHeader } from "components/SectionHeader"; import { Table } from "components/Table"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; import { DisbursementDraft } from "types"; export const DisbursementsDrafts = () => { @@ -163,7 +164,11 @@ export const DisbursementsDrafts = () => { {apiError ? ( -
{apiError}
+
) : null} diff --git a/src/pages/DisbursementsNew.tsx b/src/pages/DisbursementsNew.tsx index f52a6df..8dab578 100644 --- a/src/pages/DisbursementsNew.tsx +++ b/src/pages/DisbursementsNew.tsx @@ -29,6 +29,7 @@ import { DisbursementButtons } from "components/DisbursementButtons"; import { NotificationWithButtons } from "components/NotificationWithButtons"; import { InfoTooltip } from "components/InfoTooltip"; import { AccountBalances } from "components/AccountBalances"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; import { Disbursement, DisbursementStep } from "types"; @@ -341,16 +342,12 @@ export const DisbursementsNew = () => { : "Error" } > -
{apiError}
- {disbursementDrafts.errorExtras ? ( -
    - {Object.entries(disbursementDrafts.errorExtras).map( - ([key, value]) => ( -
  • {`${key}: ${value}`}
  • - ), - )} -
- ) : null} + ) : null} diff --git a/src/pages/DistributionAccount.tsx b/src/pages/DistributionAccount.tsx index 0ccd09d..40720fc 100644 --- a/src/pages/DistributionAccount.tsx +++ b/src/pages/DistributionAccount.tsx @@ -13,6 +13,7 @@ import { SectionHeader } from "components/SectionHeader"; import { AccountBalances } from "components/AccountBalances"; import { WalletTrustlines } from "components/WalletTrustlines"; import { LoadingContent } from "components/LoadingContent"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; import { useRedux } from "hooks/useRedux"; import { useOrgAccountInfo } from "hooks/useOrgAccountInfo"; @@ -35,7 +36,12 @@ export const DistributionAccount = () => { if (organization.errorString) { return ( - {organization.errorString} + ); } diff --git a/src/pages/ForgotPassword.tsx b/src/pages/ForgotPassword.tsx index 0df6a7e..4ee480f 100644 --- a/src/pages/ForgotPassword.tsx +++ b/src/pages/ForgotPassword.tsx @@ -11,6 +11,7 @@ import { useNavigate } from "react-router-dom"; import { useForgotPasswordLink } from "apiQueries/useForgotPasswordLink"; import { RECAPTCHA_SITE_KEY } from "constants/settings"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; export const ForgotPassword = () => { const { @@ -70,7 +71,7 @@ export const ForgotPassword = () => { {error ? ( - {error.message} + ) : null} diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 60099bc..5c98847 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -41,7 +41,9 @@ export const Home = () => { }, [dispatch, isRoleAccepted, userAccount.isAuthenticated]); const apiErrorDisbursements = - disbursements.status === "ERROR" && disbursements.errorString; + disbursements.status === "ERROR" && disbursements.errorString + ? disbursements.errorString + : undefined; const goToAnalytics = ( event: React.MouseEvent, diff --git a/src/pages/MFAuth.tsx b/src/pages/MFAuth.tsx index a6498a4..aa46828 100644 --- a/src/pages/MFAuth.tsx +++ b/src/pages/MFAuth.tsx @@ -21,6 +21,7 @@ import { } from "constants/settings"; import { useRedux } from "hooks/useRedux"; import { mfaAction, signInAction } from "store/ducks/userAccount"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; export const MFAuth = () => { const dispatch: AppDispatch = useDispatch(); @@ -101,7 +102,11 @@ export const MFAuth = () => {
{userAccount.errorString && ( - {userAccount.errorString} + )}
diff --git a/src/pages/PaymentDetails.tsx b/src/pages/PaymentDetails.tsx index 85b526b..bbc1d00 100644 --- a/src/pages/PaymentDetails.tsx +++ b/src/pages/PaymentDetails.tsx @@ -32,6 +32,8 @@ import { ReceiverStatus } from "components/ReceiverStatus"; import { AssetAmount } from "components/AssetAmount"; import { MultipleAmounts } from "components/MultipleAmounts"; import { RetryFailedPayment } from "components/RetryFailedPayment"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; + import { PaymentDetailsReceiver } from "types"; // TODO: handle loading/fetching state (create component that handles it @@ -120,7 +122,11 @@ export const PaymentDetails = () => { if (paymentError) { return ( - {`Error fetching payment details: ${paymentError.message}`} + ); } diff --git a/src/pages/Profile.tsx b/src/pages/Profile.tsx index 76d2e9f..86c0b7e 100644 --- a/src/pages/Profile.tsx +++ b/src/pages/Profile.tsx @@ -19,6 +19,7 @@ import { InfoTooltip } from "components/InfoTooltip"; import { MoreMenuButton } from "components/MoreMenuButton"; import { NotificationWithButtons } from "components/NotificationWithButtons"; import { SectionHeader } from "components/SectionHeader"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; import { Routes, USE_SSO } from "constants/settings"; import { singleUserStore } from "helpers/singleSingOn"; @@ -497,29 +498,23 @@ export const Profile = () => {
{profile.errorString ? ( -
{profile.errorString}
- {profile.errorExtras ? ( -
    - {Object.entries(profile.errorExtras).map(([key, value]) => ( -
  • {`${key}: ${value}`}
  • - ))} -
- ) : null} +
) : null} {organization.errorString ? ( -
{organization.errorString}
- {organization.errorExtras ? ( -
    - {Object.entries(organization.errorExtras).map( - ([key, value]) => ( -
  • {`${key}: ${value}`}
  • - ), - )} -
- ) : null} +
) : null} diff --git a/src/pages/ReceiverDetails.tsx b/src/pages/ReceiverDetails.tsx index e71d70b..81f886b 100644 --- a/src/pages/ReceiverDetails.tsx +++ b/src/pages/ReceiverDetails.tsx @@ -22,6 +22,7 @@ import { ReceiverWalletHistory } from "components/ReceiverWalletHistory"; import { LoadingContent } from "components/LoadingContent"; import { NotificationWithButtons } from "components/NotificationWithButtons"; import { ReceiverPayments } from "components/ReceiverPayments"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; import { useReceiversReceiverId } from "apiQueries/useReceiversReceiverId"; import { useReceiverWalletInviteSmsRetry } from "apiQueries/useReceiverWalletInviteSmsRetry"; @@ -272,7 +273,7 @@ export const ReceiverDetails = () => { }, ]} > - {smsRetryError.message} + )}
@@ -468,7 +469,11 @@ export const ReceiverDetails = () => { if (receiverDetailsError || !receiverDetails) { return ( -
{receiverDetailsError?.message || GENERIC_ERROR_MESSAGE}
+
); } diff --git a/src/pages/ReceiverDetailsEdit.tsx b/src/pages/ReceiverDetailsEdit.tsx index fe31d8f..b0c616e 100644 --- a/src/pages/ReceiverDetailsEdit.tsx +++ b/src/pages/ReceiverDetailsEdit.tsx @@ -17,6 +17,7 @@ import { SectionHeader } from "components/SectionHeader"; import { CopyWithIcon } from "components/CopyWithIcon"; import { InfoTooltip } from "components/InfoTooltip"; import { LoadingContent } from "components/LoadingContent"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; import { ReceiverDetails, ReceiverEditFields } from "types"; import { useUpdateReceiverDetails } from "apiQueries/useUpdateReceiverDetails"; @@ -143,7 +144,11 @@ export const ReceiverDetailsEdit = () => { if (receiverDetailsError || !receiverDetails) { return ( -
{receiverDetailsError?.message || GENERIC_ERROR_MESSAGE}
+
); } @@ -172,7 +177,7 @@ export const ReceiverDetailsEdit = () => {
{updateError ? ( -
{updateError?.message}
+
) : null} diff --git a/src/pages/ResetPassword.tsx b/src/pages/ResetPassword.tsx index a3c1479..0d9e71f 100644 --- a/src/pages/ResetPassword.tsx +++ b/src/pages/ResetPassword.tsx @@ -11,6 +11,7 @@ import { useNavigate } from "react-router-dom"; import { useResetPassword } from "apiQueries/useResetPassword"; import { validateNewPassword } from "helpers/validateNewPassword"; import { validatePasswordMatch } from "helpers/validatePasswordMatch"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; export const ResetPassword = () => { const { isSuccess, isLoading, error, mutateAsync, reset } = @@ -75,14 +76,7 @@ export const ResetPassword = () => { {error ? ( - {error.message} - {error?.extras ? ( -
    - {Object.entries(error?.extras).map(([key, value]) => ( -
  • {`${key}: ${value}`}
  • - ))} -
- ) : null} +
) : null} diff --git a/src/pages/SetNewPassword.tsx b/src/pages/SetNewPassword.tsx index ae0a94e..9b2b1a0 100644 --- a/src/pages/SetNewPassword.tsx +++ b/src/pages/SetNewPassword.tsx @@ -17,6 +17,7 @@ import { localStorageSessionToken } from "helpers/localStorageSessionToken"; import { useNewPassword } from "apiQueries/useNewPassword"; import { AppDispatch, resetStoreAction } from "store"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; export const SetNewPassword = () => { const { @@ -92,14 +93,7 @@ export const SetNewPassword = () => { {error ? ( - {error.message} - {error?.extras ? ( -
    - {Object.entries(error.extras).map(([key, value]) => ( -
  • {`${key}: ${value}`}
  • - ))} -
- ) : null} +
) : null} diff --git a/src/pages/SignIn.tsx b/src/pages/SignIn.tsx index e2ce17d..7a20719 100644 --- a/src/pages/SignIn.tsx +++ b/src/pages/SignIn.tsx @@ -21,6 +21,7 @@ import { } from "constants/settings"; import { useRedux } from "hooks/useRedux"; import { signInRedirect } from "helpers/singleSingOn"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; export const SignIn = () => { const dispatch: AppDispatch = useDispatch(); @@ -118,7 +119,11 @@ export const SignIn = () => { )} {!isSessionExpired && userAccount.errorString && ( - {userAccount.errorString} + )} diff --git a/src/pages/WalletProviders.tsx b/src/pages/WalletProviders.tsx index 33f8e84..6ec4258 100644 --- a/src/pages/WalletProviders.tsx +++ b/src/pages/WalletProviders.tsx @@ -12,6 +12,7 @@ import { InfoTooltip } from "components/InfoTooltip"; import { SectionHeader } from "components/SectionHeader"; import { LoadingContent } from "components/LoadingContent"; import { WalletCard } from "components/WalletCard"; +import { ErrorWithExtras } from "components/ErrorWithExtras"; import { useWallets } from "apiQueries/useWallets"; import { useUpdateWallet } from "apiQueries/useUpdateWallet"; @@ -107,7 +108,7 @@ export const WalletProviders = () => {
{walletsError || walletUpdateError ? ( - {walletsError?.message || walletUpdateError.message} + ) : null} diff --git a/src/store/ducks/disbursementDetails.ts b/src/store/ducks/disbursementDetails.ts index 7e5bdf4..a8219f1 100644 --- a/src/store/ducks/disbursementDetails.ts +++ b/src/store/ducks/disbursementDetails.ts @@ -2,11 +2,11 @@ import { PayloadAction, createAsyncThunk, createSlice } from "@reduxjs/toolkit"; import { RootState } from "store"; import { getDisbursementDetails } from "api/getDisbursementDetails"; import { getDisbursementReceivers } from "api/getDisbursementReceivers"; -import { handleApiErrorString } from "api/handleApiErrorString"; import { patchDisbursementStatus } from "api/patchDisbursementStatus"; import { endSessionIfTokenInvalid } from "helpers/endSessionIfTokenInvalid"; import { refreshSessionToken } from "helpers/refreshSessionToken"; import { formatDisbursement } from "helpers/formatDisbursements"; +import { normalizeApiError } from "helpers/normalizeApiError"; import { ApiDisbursementReceiver, ApiDisbursementReceivers, @@ -38,7 +38,8 @@ export const getDisbursementDetailsAction = createAsyncThunk< return formatDisbursement(disbursementDetails); } catch (error: unknown) { - const errorString = handleApiErrorString(error as ApiError); + const apiError = normalizeApiError(error as ApiError); + const errorString = apiError.message; endSessionIfTokenInvalid(errorString, dispatch); return rejectWithValue({ @@ -72,7 +73,8 @@ export const getDisbursementReceiversAction = createAsyncThunk< searchParams: newParams, }; } catch (error: unknown) { - const errorString = handleApiErrorString(error as ApiError); + const apiError = normalizeApiError(error as ApiError); + const errorString = apiError.message; endSessionIfTokenInvalid(errorString, dispatch); return rejectWithValue({ @@ -98,7 +100,8 @@ export const pauseOrStartDisbursementAction = createAsyncThunk< return { status: newStatus, message }; } catch (error: unknown) { - const errorString = handleApiErrorString(error as ApiError); + const apiError = normalizeApiError(error as ApiError); + const errorString = apiError.message; endSessionIfTokenInvalid(errorString, dispatch); return rejectWithValue({ diff --git a/src/store/ducks/disbursementDrafts.ts b/src/store/ducks/disbursementDrafts.ts index c86d41e..dc90b4a 100644 --- a/src/store/ducks/disbursementDrafts.ts +++ b/src/store/ducks/disbursementDrafts.ts @@ -4,10 +4,10 @@ import { getDisbursementDrafts } from "api/getDisbursementDrafts"; import { postDisbursement } from "api/postDisbursement"; import { postDisbursementFile } from "api/postDisbursementFile"; import { patchDisbursementStatus } from "api/patchDisbursementStatus"; -import { handleApiErrorString } from "api/handleApiErrorString"; import { formatDisbursement } from "helpers/formatDisbursements"; import { endSessionIfTokenInvalid } from "helpers/endSessionIfTokenInvalid"; import { refreshSessionToken } from "helpers/refreshSessionToken"; +import { normalizeApiError } from "helpers/normalizeApiError"; import { ApiError, Disbursement, @@ -46,7 +46,7 @@ export const getDisbursementDraftsAction = createAsyncThunk< pagination, }; } catch (error: unknown) { - const errorString = handleApiErrorString(error as ApiError); + const errorString = normalizeApiError(error as ApiError).message; endSessionIfTokenInvalid(errorString, dispatch); return rejectWithValue({ @@ -85,13 +85,13 @@ export const saveDisbursementDraftAction = createAsyncThunk< return draftId; } } catch (error: unknown) { - const err = error as ApiError; - const errorString = handleApiErrorString(err); + const apiError = normalizeApiError(error as ApiError); + const errorString = apiError.message; endSessionIfTokenInvalid(errorString, dispatch); return rejectWithValue({ errorString: `Error saving draft: ${errorString}`, - errorExtras: err?.extras, + errorExtras: apiError?.extras, // Need to save draft ID if it failed because of CSV upload newDraftId: draftId, }); @@ -127,13 +127,13 @@ export const submitDisbursementNewDraftAction = createAsyncThunk< return draftId; } catch (error: unknown) { - const err = error as ApiError; - const errorString = handleApiErrorString(err); + const apiError = normalizeApiError(error as ApiError); + const errorString = apiError.message; endSessionIfTokenInvalid(errorString, dispatch); return rejectWithValue({ errorString: `Error submitting disbursement: ${errorString}`, - errorExtras: err?.extras, + errorExtras: apiError?.extras, // Need to save draft ID if it failed because of CSV upload newDraftId: draftId, }); @@ -158,13 +158,13 @@ export const saveNewCsvFileAction = createAsyncThunk< return true; } catch (error: unknown) { - const err = error as ApiError; - const errorString = handleApiErrorString(err); + const apiError = normalizeApiError(error as ApiError); + const errorString = apiError.message; endSessionIfTokenInvalid(errorString, dispatch); return rejectWithValue({ errorString: `Error uploading new CSV file: ${errorString}`, - errorExtras: err?.extras, + errorExtras: apiError?.extras, }); } }, @@ -209,13 +209,13 @@ export const submitDisbursementSavedDraftAction = createAsyncThunk< return draftId; } catch (error: unknown) { - const err = error as ApiError; - const errorString = handleApiErrorString(err); + const apiError = normalizeApiError(error as ApiError); + const errorString = apiError.message; endSessionIfTokenInvalid(errorString, dispatch); return rejectWithValue({ errorString: `Error submitting disbursement: ${errorString}`, - errorExtras: err?.extras, + errorExtras: apiError?.extras, // Need to save draft ID if it failed because of CSV upload newDraftId: draftId, }); diff --git a/src/store/ducks/disbursements.ts b/src/store/ducks/disbursements.ts index df5f8e6..4b395ba 100644 --- a/src/store/ducks/disbursements.ts +++ b/src/store/ducks/disbursements.ts @@ -1,10 +1,10 @@ import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; import { RootState } from "store"; import { getDisbursements } from "api/getDisbursements"; -import { handleApiErrorString } from "api/handleApiErrorString"; import { formatDisbursements } from "helpers/formatDisbursements"; import { endSessionIfTokenInvalid } from "helpers/endSessionIfTokenInvalid"; import { refreshSessionToken } from "helpers/refreshSessionToken"; +import { normalizeApiError } from "helpers/normalizeApiError"; import { ApiDisbursements, ApiError, @@ -28,7 +28,8 @@ export const getDisbursementsAction = createAsyncThunk< return disbursements; } catch (error: unknown) { - const errorString = handleApiErrorString(error as ApiError); + const apiError = normalizeApiError(error as ApiError); + const errorString = apiError.message; endSessionIfTokenInvalid(errorString, dispatch); return rejectWithValue({ @@ -62,7 +63,8 @@ export const getDisbursementsWithParamsAction = createAsyncThunk< searchParams: newParams, }; } catch (error: unknown) { - const errorString = handleApiErrorString(error as ApiError); + const apiError = normalizeApiError(error as ApiError); + const errorString = apiError.message; endSessionIfTokenInvalid(errorString, dispatch); return rejectWithValue({ diff --git a/src/store/ducks/organization.ts b/src/store/ducks/organization.ts index a71c107..9867e8b 100644 --- a/src/store/ducks/organization.ts +++ b/src/store/ducks/organization.ts @@ -1,12 +1,12 @@ import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; import { RootState } from "store"; -import { handleApiErrorString } from "api/handleApiErrorString"; import { getOrgInfo } from "api/getOrgInfo"; import { patchOrgInfo } from "api/patchOrgInfo"; import { getOrgLogo } from "api/getOrgLogo"; import { getStellarAccountInfo } from "api/getStellarAccountInfo"; import { endSessionIfTokenInvalid } from "helpers/endSessionIfTokenInvalid"; import { refreshSessionToken } from "helpers/refreshSessionToken"; +import { normalizeApiError } from "helpers/normalizeApiError"; import { ApiError, ApiOrgInfo, @@ -31,7 +31,8 @@ export const getOrgInfoAction = createAsyncThunk< return orgInfo; } catch (error: unknown) { - const errorString = handleApiErrorString(error as ApiError); + const apiError = normalizeApiError(error as ApiError); + const errorString = apiError.message; endSessionIfTokenInvalid(errorString, dispatch); return rejectWithValue({ @@ -62,13 +63,13 @@ export const updateOrgInfoAction = createAsyncThunk< }); return orgInfo.message; } catch (error: unknown) { - const err = error as ApiError; - const errorString = handleApiErrorString(err); + const apiError = normalizeApiError(error as ApiError); + const errorString = apiError.message; endSessionIfTokenInvalid(errorString, dispatch); return rejectWithValue({ errorString: `Error updating organization info: ${errorString}`, - errorExtras: err?.extras, + errorExtras: apiError?.extras, }); } }, @@ -82,7 +83,8 @@ export const getOrgLogoAction = createAsyncThunk< try { return await getOrgLogo(); } catch (error: unknown) { - const errorString = handleApiErrorString(error as ApiError); + const apiError = normalizeApiError(error as ApiError); + const errorString = apiError.message; endSessionIfTokenInvalid(errorString, dispatch); return rejectWithValue({ @@ -108,7 +110,8 @@ export const getStellarAccountAction = createAsyncThunk< return { address: accountInfo.id, balances }; } catch (error: unknown) { - const errorString = handleApiErrorString(error as ApiError); + const apiError = normalizeApiError(error as ApiError); + const errorString = apiError.message; return rejectWithValue({ errorString: `Error fetching Stellar account info: ${errorString}`, diff --git a/src/store/ducks/profile.ts b/src/store/ducks/profile.ts index 58baa3e..6b75573 100644 --- a/src/store/ducks/profile.ts +++ b/src/store/ducks/profile.ts @@ -1,10 +1,10 @@ import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; import { RootState } from "store"; -import { handleApiErrorString } from "api/handleApiErrorString"; import { getProfileInfo } from "api/getProfileInfo"; import { patchProfileInfo } from "api/patchProfileInfo"; import { endSessionIfTokenInvalid } from "helpers/endSessionIfTokenInvalid"; import { refreshSessionToken } from "helpers/refreshSessionToken"; +import { normalizeApiError } from "helpers/normalizeApiError"; import { ApiError, ApiProfileInfo, @@ -27,7 +27,8 @@ export const getProfileInfoAction = createAsyncThunk< return profileInfo; } catch (error: unknown) { - const errorString = handleApiErrorString(error as ApiError); + const apiError = normalizeApiError(error as ApiError); + const errorString = apiError.message; endSessionIfTokenInvalid(errorString, dispatch); return rejectWithValue({ @@ -57,13 +58,13 @@ export const updateProfileInfoAction = createAsyncThunk< }); return profileInfo.message; } catch (error: unknown) { - const err = error as ApiError; - const errorString = handleApiErrorString(err); + const apiError = normalizeApiError(error as ApiError); + const errorString = apiError.message; endSessionIfTokenInvalid(errorString, dispatch); return rejectWithValue({ errorString: `Error updating profile info: ${errorString}`, - errorExtras: err?.extras, + errorExtras: apiError?.extras, }); } }, diff --git a/src/store/ducks/userAccount.ts b/src/store/ducks/userAccount.ts index c888aa8..d038530 100644 --- a/src/store/ducks/userAccount.ts +++ b/src/store/ducks/userAccount.ts @@ -4,8 +4,8 @@ import { singleSignOnAction } from "store/ducks/singleSignOnUserAccount"; import { authLogin } from "api/authLogin"; import { mfAuth } from "api/mfAuth"; import { refreshToken } from "api/refreshToken"; -import { handleApiErrorString } from "api/handleApiErrorString"; import { endSessionIfTokenInvalid } from "helpers/endSessionIfTokenInvalid"; +import { normalizeApiError } from "helpers/normalizeApiError"; import { ApiError, JwtUser, @@ -34,7 +34,8 @@ export const signInAction = createAsyncThunk< ); return response; } catch (error: unknown) { - const errorString = handleApiErrorString(error as ApiError); + const apiError = normalizeApiError(error as ApiError); + const errorString = apiError.message; return rejectWithValue({ errorString, @@ -56,7 +57,8 @@ export const refreshTokenAction = createAsyncThunk< const newToken = await refreshToken(token); return newToken; } catch (error: unknown) { - const errorString = handleApiErrorString(error as ApiError); + const apiError = normalizeApiError(error as ApiError); + const errorString = apiError.message; endSessionIfTokenInvalid(errorString, dispatch); return rejectWithValue({ @@ -90,7 +92,8 @@ export const mfaAction = createAsyncThunk< ); return response; } catch (error: unknown) { - const errorString = handleApiErrorString(error as ApiError); + const apiError = normalizeApiError(error as ApiError); + const errorString = apiError.message; return rejectWithValue({ errorString,