diff --git a/web/src/components/NumberDisplay.tsx b/web/src/components/NumberDisplay.tsx index ee67ec43e..fa0951825 100644 --- a/web/src/components/NumberDisplay.tsx +++ b/web/src/components/NumberDisplay.tsx @@ -1,6 +1,9 @@ import React from "react"; import { Tooltip } from "@kleros/ui-components-library"; + +import { commify } from "utils/commify"; + interface INumberDisplay { value: string | number; unit?: string; @@ -31,10 +34,11 @@ const NumberDisplay: React.FC = ({ isCurrency = false, }) => { const parsedValue = Number(value); - const formattedValue = getFormattedValue(parsedValue, decimals); + const formattedValue = commify(getFormattedValue(parsedValue, decimals)); const tooltipValue = isCurrency ? `${unit} ${value}` : `${value} ${unit}`; const displayUnit = showUnitInDisplay ? unit : ""; const displayValue = isCurrency ? `${displayUnit} ${formattedValue}` : `${formattedValue} ${displayUnit}`; + return ( {displayValue} diff --git a/web/src/hooks/queries/useCasesQuery.ts b/web/src/hooks/queries/useCasesQuery.ts index 56e6cfe2f..ad651fee0 100644 --- a/web/src/hooks/queries/useCasesQuery.ts +++ b/web/src/hooks/queries/useCasesQuery.ts @@ -3,6 +3,7 @@ import { Address } from "viem"; import { useGraphqlBatcher } from "context/GraphqlBatcher"; import { isUndefined } from "utils/index"; +import { sanitizeFilter } from "utils/sanitizeFilter"; import { graphql } from "src/graphql"; import { @@ -71,17 +72,18 @@ const myCasesQueryWhere = graphql(` export const useCasesQuery = (skip = 0, first = 3, where?: Dispute_Filter, sortOrder?: OrderDirection) => { const { graphqlBatcher } = useGraphqlBatcher(); + const sanitizedWhere = sanitizeFilter(where); return useQuery({ - queryKey: [`useCasesQuery`, skip, where, sortOrder, first], + queryKey: [`useCasesQuery`, skip, sanitizedWhere, sortOrder, first], queryFn: async () => await graphqlBatcher.fetch({ id: crypto.randomUUID(), - document: isUndefined(where) ? casesQuery : casesQueryWhere, + document: isUndefined(sanitizedWhere) ? casesQuery : casesQueryWhere, variables: { first, skip, - where, + where: sanitizedWhere, orderDirection: sortOrder ?? "desc", }, }), @@ -89,20 +91,20 @@ export const useCasesQuery = (skip = 0, first = 3, where?: Dispute_Filter, sortO }; export const useMyCasesQuery = (user?: Address, skip = 0, where?: Dispute_Filter, sortOrder?: OrderDirection) => { - const isEnabled = !isUndefined(user); const { graphqlBatcher } = useGraphqlBatcher(); + const sanitizedWhere = sanitizeFilter(where); return useQuery({ - queryKey: [`useMyCasesQuery`, user, skip, where, sortOrder], - enabled: isEnabled, + queryKey: ["useMyCasesQuery", user, skip, sanitizedWhere, sortOrder], + enabled: !isUndefined(user), queryFn: async () => await graphqlBatcher.fetch({ id: crypto.randomUUID(), - document: isUndefined(where) ? myCasesQuery : myCasesQueryWhere, + document: isUndefined(sanitizedWhere) ? myCasesQuery : myCasesQueryWhere, variables: { skip, id: user?.toLowerCase(), - where, + where: sanitizedWhere, orderDirection: sortOrder ?? "desc", }, }), diff --git a/web/src/hooks/queries/useUser.ts b/web/src/hooks/queries/useUser.ts index 5da4bc1e0..b03090916 100644 --- a/web/src/hooks/queries/useUser.ts +++ b/web/src/hooks/queries/useUser.ts @@ -3,6 +3,7 @@ import { Address } from "viem"; import { useGraphqlBatcher } from "context/GraphqlBatcher"; +import { sanitizeFilter } from "utils/sanitizeFilter"; import { STALE_TIME } from "src/consts"; import { graphql } from "src/graphql"; import { UserQuery, Dispute_Filter, UserDisputeFilterQuery, UserDetailsFragment } from "src/graphql/graphql"; @@ -52,19 +53,20 @@ const userQueryDisputeFilter = graphql(` `); export const useUserQuery = (address?: Address, where?: Dispute_Filter) => { + const sanitizedWhere = sanitizeFilter(where); const isEnabled = address !== undefined; - const query = where ? userQueryDisputeFilter : userQuery; + const query = sanitizedWhere ? userQueryDisputeFilter : userQuery; const { graphqlBatcher } = useGraphqlBatcher(); return useQuery({ - queryKey: [`userQuery${address?.toLowerCase()}`], + queryKey: ["userQuery", address?.toLowerCase(), sanitizedWhere], enabled: isEnabled, staleTime: STALE_TIME, queryFn: async () => await graphqlBatcher.fetch({ id: crypto.randomUUID(), document: query, - variables: { address: address?.toLowerCase(), where }, + variables: { address: address?.toLowerCase(), where: sanitizedWhere }, }), }); }; diff --git a/web/src/pages/Profile/index.tsx b/web/src/pages/Profile/index.tsx index 8a329a719..109046748 100644 --- a/web/src/pages/Profile/index.tsx +++ b/web/src/pages/Profile/index.tsx @@ -11,7 +11,7 @@ import { isUndefined } from "utils/index"; import { decodeURIFilter, useRootPath } from "utils/uri"; import { DisputeDetailsFragment, useMyCasesQuery } from "queries/useCasesQuery"; import { useUserQuery } from "queries/useUser"; -import { OrderDirection } from "src/graphql/graphql"; +import { Dispute_Filter, OrderDirection, UserDetailsFragment } from "src/graphql/graphql"; import CasesDisplay from "components/CasesDisplay"; import ConnectWallet from "components/ConnectWallet"; @@ -50,6 +50,33 @@ const ConnectWalletContainer = styled.div` color: ${({ theme }) => theme.primaryText}; `; +const calculateStats = (user: UserDetailsFragment, filter: Dispute_Filter) => { + const toInt = (v) => Number(v) || 0; + let totalCases, ruledCases; + + if (!user) { + totalCases = 0; + ruledCases = 0; + } else if (filter?.period === "appeal") { + totalCases = toInt(user.totalAppealingDisputes); + ruledCases = 0; + } else if (filter?.ruled === true) { + totalCases = toInt(user.totalResolvedDisputes); + ruledCases = totalCases; + } else if (filter?.ruled === false) { + totalCases = toInt(user.disputes?.length); + ruledCases = 0; + } else { + totalCases = toInt(user.disputes?.length); + ruledCases = toInt(user.totalResolvedDisputes); + } + + return { + totalCases, + ruledCases, + }; +}; + const Profile: React.FC = () => { const { isConnected, address: connectedAddress } = useAccount(); const { page, order, filter } = useParams(); @@ -69,8 +96,10 @@ const Profile: React.FC = () => { order === "asc" ? OrderDirection.Asc : OrderDirection.Desc ); const { data: userData } = useUserQuery(addressToQuery, decodedFilter); - const totalCases = userData?.user?.disputes.length; - const totalResolvedCases = parseInt(userData?.user?.totalResolvedDisputes); + const { totalCases, ruledCases: totalResolvedCases } = useMemo( + () => calculateStats(userData?.user, decodedFilter), + [userData?.user, decodedFilter] + ); const totalPages = useMemo( () => (!isUndefined(totalCases) ? Math.ceil(totalCases / casesPerPage) : 1), [totalCases, casesPerPage] diff --git a/web/src/utils/sanitizeFilter.ts b/web/src/utils/sanitizeFilter.ts new file mode 100644 index 000000000..2abce80f0 --- /dev/null +++ b/web/src/utils/sanitizeFilter.ts @@ -0,0 +1,5 @@ +export const sanitizeFilter = >(f?: T) => { + if (!f) return undefined as unknown as T; + const cleaned = Object.fromEntries(Object.entries(f).filter(([_, v]) => !(Array.isArray(v) && v.length === 0))); + return Object.keys(cleaned).length ? (cleaned as T) : undefined; +};