From 7881c887682ead662c4cb7aba0d5eefe82f9b88f Mon Sep 17 00:00:00 2001 From: BoHsuu Date: Mon, 23 Feb 2026 14:10:38 +0700 Subject: [PATCH 1/2] refactor(utils): use formatErrorMessage consistently for error handling --- packages/nextjs/components/Batch/BatchContainer.tsx | 2 +- packages/nextjs/components/Transfer/TransferContainer.tsx | 3 ++- packages/nextjs/components/modals/ClaimRewardModal.tsx | 3 ++- .../nextjs/components/modals/EditAccountModal/index.tsx | 3 ++- .../nextjs/hooks/app/transaction/useBatchTransaction.ts | 3 ++- .../nextjs/hooks/app/transaction/useSignerTransaction.ts | 7 ++++--- .../nextjs/hooks/app/transaction/useTransactionVote.ts | 7 ++++--- .../hooks/app/transaction/useTransferTransaction.ts | 3 ++- packages/nextjs/hooks/app/useAuth.ts | 3 ++- packages/nextjs/hooks/app/useAuthProof.ts | 3 ++- packages/nextjs/lib/form/utils.ts | 8 ++++---- packages/nextjs/services/api/apiClient.ts | 3 ++- 12 files changed, 29 insertions(+), 19 deletions(-) diff --git a/packages/nextjs/components/Batch/BatchContainer.tsx b/packages/nextjs/components/Batch/BatchContainer.tsx index e0e607bc..c1fdacc4 100644 --- a/packages/nextjs/components/Batch/BatchContainer.tsx +++ b/packages/nextjs/components/Batch/BatchContainer.tsx @@ -397,7 +397,7 @@ export default function BatchContainer() { tokenSymbol: token.symbol, }; }), - [selectedBatchItems, getToken], + [selectedBatchItems, getToken, network], ); const handleCloseDrawer = useCallback(() => { diff --git a/packages/nextjs/components/Transfer/TransferContainer.tsx b/packages/nextjs/components/Transfer/TransferContainer.tsx index 0fbf8729..d00825c2 100644 --- a/packages/nextjs/components/Transfer/TransferContainer.tsx +++ b/packages/nextjs/components/Transfer/TransferContainer.tsx @@ -12,6 +12,7 @@ import { useNetworkTokens } from "~~/hooks/app/useNetworkTokens"; import { useTokenBalances } from "~~/hooks/app/useTokenBalance"; import { useZodForm } from "~~/hooks/form"; import { TransferFormData, transferSchema } from "~~/lib/form"; +import { formatErrorMessage } from "~~/lib/form/utils"; import { useAccountStore, useIdentityStore } from "~~/services/store"; import { notification } from "~~/utils/scaffold-eth"; @@ -120,7 +121,7 @@ export default function TransferContainer() { setSelectedContactId(null); } catch (error: any) { console.error("Add to batch error:", error); - notification.error(error.message || "Failed to add to batch"); + notification.error(formatErrorMessage(error, "Failed to add to batch")); } }; diff --git a/packages/nextjs/components/modals/ClaimRewardModal.tsx b/packages/nextjs/components/modals/ClaimRewardModal.tsx index ee626679..f146f69a 100644 --- a/packages/nextjs/components/modals/ClaimRewardModal.tsx +++ b/packages/nextjs/components/modals/ClaimRewardModal.tsx @@ -6,6 +6,7 @@ import ModalContainer from "./ModalContainer"; import { useWalletClient } from "wagmi"; import { Button } from "~~/components/ui/button"; import { useClaimRewards, useClaimSummary } from "~~/hooks/api/useClaim"; +import { formatErrorMessage } from "~~/lib/form/utils"; import { ModalProps } from "~~/types/modal"; import { chain } from "~~/utils/network-config"; @@ -63,7 +64,7 @@ const ClaimRewardModal: React.FC = ({ isOpen, onClose, we setTxHash(result.txHash); setState("success"); } catch (error: any) { - setErrorMessage(error?.message || "Failed to claim rewards. Please try again."); + setErrorMessage(formatErrorMessage(error, "Failed to claim rewards. Please try again.")); setState("error"); } }; diff --git a/packages/nextjs/components/modals/EditAccountModal/index.tsx b/packages/nextjs/components/modals/EditAccountModal/index.tsx index 5a211b97..62a7f3e4 100644 --- a/packages/nextjs/components/modals/EditAccountModal/index.tsx +++ b/packages/nextjs/components/modals/EditAccountModal/index.tsx @@ -7,6 +7,7 @@ import SubmittingStep from "./SubmittingStep"; import { ActionMode, ExistingSigner, ModalStep, PendingSigner } from "./types"; import ModalContainer from "~~/components/modals/ModalContainer"; import { useAccount, useMetaMultiSigWallet, useSignerTransaction } from "~~/hooks"; +import { formatErrorMessage } from "~~/lib/form/utils"; import { useIdentityStore, useSidebarStore } from "~~/services/store"; import { ModalProps } from "~~/types/modal"; import { notification } from "~~/utils/scaffold-eth"; @@ -146,7 +147,7 @@ const EditAccountModal: React.FC = ({ isOpen, onClose }) => { closeManageAccounts(); } catch (error: any) { console.error("Failed to submit proposal:", error); - notification.error(error.message || "Failed to submit proposal"); + notification.error(formatErrorMessage(error, "Failed to submit proposal")); setStep("confirm"); // Back to confirm on error setIsSubmitting(false); } diff --git a/packages/nextjs/hooks/app/transaction/useBatchTransaction.ts b/packages/nextjs/hooks/app/transaction/useBatchTransaction.ts index 26d0278e..47fcdc02 100644 --- a/packages/nextjs/hooks/app/transaction/useBatchTransaction.ts +++ b/packages/nextjs/hooks/app/transaction/useBatchTransaction.ts @@ -4,6 +4,7 @@ import { useWalletClient } from "wagmi"; import { useMetaMultiSigWallet } from "~~/hooks"; import { useCreateTransaction, useReserveNonce } from "~~/hooks/api"; import { useGenerateProof } from "~~/hooks/app/useGenerateProof"; +import { formatErrorMessage } from "~~/lib/form/utils"; import { useIdentityStore } from "~~/services/store"; import { notification } from "~~/utils/scaffold-eth"; @@ -100,7 +101,7 @@ export const useBatchTransaction = (options?: UseBatchTransactionOptions) => { options?.onSuccess?.(); } catch (error: any) { console.error("Propose batch error:", error); - notification.error(error.message || "Failed to propose batch"); + notification.error(formatErrorMessage(error, "Failed to propose batch")); } finally { setIsLoading(false); setLoadingState(""); diff --git a/packages/nextjs/hooks/app/transaction/useSignerTransaction.ts b/packages/nextjs/hooks/app/transaction/useSignerTransaction.ts index 08bd83be..17a617cd 100644 --- a/packages/nextjs/hooks/app/transaction/useSignerTransaction.ts +++ b/packages/nextjs/hooks/app/transaction/useSignerTransaction.ts @@ -3,6 +3,7 @@ import { SignerData, TxType, encodeAddSigners, encodeRemoveSigners, encodeUpdate import { useWalletClient } from "wagmi"; import { useGenerateProof, useMetaMultiSigWallet, useWalletCommitments, useWalletThreshold } from "~~/hooks"; import { useCreateTransaction, useReserveNonce } from "~~/hooks/api/useTransaction"; +import { formatErrorMessage } from "~~/lib/form/utils"; import { notification } from "~~/utils/scaffold-eth"; interface UseSignerTransactionOptions { @@ -107,7 +108,7 @@ export const useSignerTransaction = (options?: UseSignerTransactionOptions) => { options?.onSuccess?.(); } catch (error: any) { console.error("Failed to add signer:", error); - notification.error(error.message || "Failed to add signer"); + notification.error(formatErrorMessage(error, "Failed to add signer")); } finally { setIsLoading(false); setLoadingState(""); @@ -159,7 +160,7 @@ export const useSignerTransaction = (options?: UseSignerTransactionOptions) => { options?.onSuccess?.(); } catch (error: any) { console.error("Failed to remove signer:", error); - notification.error(error.message || "Failed to remove signer"); + notification.error(formatErrorMessage(error, "Failed to remove signer")); } finally { setIsLoading(false); setLoadingState(""); @@ -188,7 +189,7 @@ export const useSignerTransaction = (options?: UseSignerTransactionOptions) => { options?.onSuccess?.(); } catch (error: any) { console.error("Failed to update threshold:", error); - notification.error(error.message || "Failed to update threshold"); + notification.error(formatErrorMessage(error, "Failed to update threshold")); } finally { setIsLoading(false); setLoadingState(""); diff --git a/packages/nextjs/hooks/app/transaction/useTransactionVote.ts b/packages/nextjs/hooks/app/transaction/useTransactionVote.ts index 11aceb38..9475cc82 100644 --- a/packages/nextjs/hooks/app/transaction/useTransactionVote.ts +++ b/packages/nextjs/hooks/app/transaction/useTransactionVote.ts @@ -16,6 +16,7 @@ import { useWalletClient } from "wagmi"; import { accountKeys, useMetaMultiSigWallet, userKeys } from "~~/hooks"; import { useApproveTransaction, useDenyTransaction, useExecuteTransaction } from "~~/hooks/api/useTransaction"; import { useGenerateProof } from "~~/hooks/app/useGenerateProof"; +import { formatErrorMessage } from "~~/lib/form/utils"; import { useIdentityStore } from "~~/services/store/useIdentityStore"; import { notification } from "~~/utils/scaffold-eth"; @@ -175,7 +176,7 @@ export const useTransactionVote = (options?: UseTransactionVoteOptions) => { options?.onSuccess?.(); } catch (error: any) { console.error("Approve error:", error); - notification.error(error.message || "Failed to approve"); + notification.error(formatErrorMessage(error, "Failed to approve")); } finally { setIsLoading(false); setLoadingState(""); @@ -207,7 +208,7 @@ export const useTransactionVote = (options?: UseTransactionVoteOptions) => { options?.onSuccess?.(); } catch (error: any) { console.error("Deny error:", error); - notification.error(error.message || "Failed to deny"); + notification.error(formatErrorMessage(error, "Failed to deny")); } finally { setIsLoading(false); setLoadingState(""); @@ -245,7 +246,7 @@ export const useTransactionVote = (options?: UseTransactionVoteOptions) => { options?.onSuccess?.(); } catch (error: any) { console.error("Execute error:", error); - notification.error(error.message || "Failed to execute"); + notification.error(formatErrorMessage(error, "Failed to execute")); } finally { setIsLoading(false); setLoadingState(""); diff --git a/packages/nextjs/hooks/app/transaction/useTransferTransaction.ts b/packages/nextjs/hooks/app/transaction/useTransferTransaction.ts index fc8e72a7..e16cebd6 100644 --- a/packages/nextjs/hooks/app/transaction/useTransferTransaction.ts +++ b/packages/nextjs/hooks/app/transaction/useTransferTransaction.ts @@ -5,6 +5,7 @@ import { useWalletClient } from "wagmi"; import { useMetaMultiSigWallet } from "~~/hooks"; import { useCreateTransaction, useReserveNonce } from "~~/hooks/api/useTransaction"; import { useGenerateProof } from "~~/hooks/app/useGenerateProof"; +import { formatErrorMessage } from "~~/lib/form/utils"; import { notification } from "~~/utils/scaffold-eth"; interface TransferParams { @@ -101,7 +102,7 @@ export const useTransferTransaction = (options?: UseTransferTransactionOptions) options?.onSuccess?.(); } catch (error: any) { console.error("Transfer error:", error); - notification.error(error.message || "Failed to create transfer"); + notification.error(formatErrorMessage(error, "Failed to create transfer")); } finally { setIsLoading(false); setLoadingState(""); diff --git a/packages/nextjs/hooks/app/useAuth.ts b/packages/nextjs/hooks/app/useAuth.ts index c894c740..cb06b944 100644 --- a/packages/nextjs/hooks/app/useAuth.ts +++ b/packages/nextjs/hooks/app/useAuth.ts @@ -2,6 +2,7 @@ import { useCallback, useState } from "react"; import { userKeys } from "../api"; import { useAuthProof } from "./useAuthProof"; import { useQueryClient } from "@tanstack/react-query"; +import { formatErrorMessage } from "~~/lib/form/utils"; import { authApi } from "~~/services/api"; import { useIdentityStore } from "~~/services/store"; @@ -52,7 +53,7 @@ export const useAuth = () => { return true; } catch (err: any) { - setError(err.message || "Login failed"); + setError(formatErrorMessage(err, "Login failed")); return false; } finally { setIsLoggingIn(false); diff --git a/packages/nextjs/hooks/app/useAuthProof.ts b/packages/nextjs/hooks/app/useAuthProof.ts index 733e2e3a..82e39f1f 100644 --- a/packages/nextjs/hooks/app/useAuthProof.ts +++ b/packages/nextjs/hooks/app/useAuthProof.ts @@ -1,5 +1,6 @@ import { useState } from "react"; import { useWalletClient } from "wagmi"; +import { formatErrorMessage } from "~~/lib/form/utils"; import { createCommitment, createSecret } from "~~/utils/multisig"; interface AuthProofResult { @@ -69,7 +70,7 @@ export const useAuthProof = () => { walletAddress, // For analytics only - NOT stored in database }; } catch (err: any) { - setError(err.message || "Failed to generate auth proof"); + setError(formatErrorMessage(err, "Failed to generate auth proof")); return null; } finally { setIsGenerating(false); diff --git a/packages/nextjs/lib/form/utils.ts b/packages/nextjs/lib/form/utils.ts index 7da2181f..3f3613f3 100644 --- a/packages/nextjs/lib/form/utils.ts +++ b/packages/nextjs/lib/form/utils.ts @@ -94,13 +94,13 @@ export function getDirtyFields(form: UseFormReturn): P /** * Format error message for display */ -export function formatErrorMessage(error: unknown): string { +export function formatErrorMessage(error: unknown, defaultMessage?: string): string { if (typeof error === "string") return error; - if (error instanceof Error) return error.message; - if (typeof error === "object" && error !== null && "message" in error) { + if (error instanceof Error && error.message) return error.message; + if (typeof error === "object" && error !== null && "message" in error && error.message) { return String(error.message); } - return "An unknown error occurred"; + return defaultMessage ?? "An unknown error occurred"; } /** diff --git a/packages/nextjs/services/api/apiClient.ts b/packages/nextjs/services/api/apiClient.ts index 3bab29d1..e347f2fb 100644 --- a/packages/nextjs/services/api/apiClient.ts +++ b/packages/nextjs/services/api/apiClient.ts @@ -2,6 +2,7 @@ import { useIdentityStore } from "../store"; import { authApi } from "./authApi"; import axios, { AxiosError, AxiosInstance, AxiosResponse, InternalAxiosRequestConfig } from "axios"; import { API_BASE_URL } from "~~/constants"; +import { formatErrorMessage } from "~~/lib/form/utils"; const AUTHORIZATION_HEADER = (accessToken: string) => `Bearer ${accessToken}`; const ZK_TIMEOUT = 600000; // 10 minutes for ZK proof generation + verification @@ -103,7 +104,7 @@ apiClient.interceptors.response.use( throw new Error("Network Error - Please check your connection"); } else { console.error("❌ Error:", error.message); - throw new Error(error.message || "An unexpected error occurred"); + throw new Error(formatErrorMessage(error, "An unexpected error occurred")); } }, ); From b45688365829f277d0a5465a7384a22cd24662b6 Mon Sep 17 00:00:00 2001 From: BoHsuu Date: Mon, 23 Feb 2026 16:47:02 +0700 Subject: [PATCH 2/2] Update example dates in AnalyticsReportDto for clarity --- packages/backend/src/admin/dto/analytics-report.dto.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/admin/dto/analytics-report.dto.ts b/packages/backend/src/admin/dto/analytics-report.dto.ts index fa7a49de..3c0f2ec0 100644 --- a/packages/backend/src/admin/dto/analytics-report.dto.ts +++ b/packages/backend/src/admin/dto/analytics-report.dto.ts @@ -5,7 +5,7 @@ import { Transform } from 'class-transformer'; export class AnalyticsReportDto { @ApiPropertyOptional({ description: 'Start date (inclusive)', - example: '2026-01-01', + example: '2026-01-29', }) @IsOptional() @IsDateString() @@ -13,7 +13,7 @@ export class AnalyticsReportDto { @ApiPropertyOptional({ description: 'End date (inclusive)', - example: '2026-02-01', + example: '2027-01-29', }) @IsOptional() @IsDateString()