diff --git a/cspell.json b/cspell.json index 001e6c28f..5fc8d42a3 100644 --- a/cspell.json +++ b/cspell.json @@ -23,7 +23,6 @@ "pnpm-lock.yaml" ], "words": [ - "lokalise", "arrayify", "chartjs", "clsx", @@ -43,6 +42,7 @@ "JWTs", "keccak", "Localise", + "lokalise", "merkle", "nextjs", "nullifer", @@ -58,6 +58,7 @@ "timestamptz", "toastify", "unarchived", + "USDCE", "Worldcoin", "zustand" ] diff --git a/web/api/v2/public/apps/index.ts b/web/api/v2/public/apps/index.ts index 4452ea63d..6888e74d9 100644 --- a/web/api/v2/public/apps/index.ts +++ b/web/api/v2/public/apps/index.ts @@ -14,14 +14,6 @@ import { } from "./graphql/get-app-rankings.generated"; import { getSdk as getHighlightsSdk } from "./graphql/get-app-web-highlights.generated"; -export type GetAppsResponse = { - app_rankings: { - top_apps: ReturnType; - highlights: ReturnType; - }; - categories: ReturnType; -}; - const queryParamsSchema = yup.object({ page: yup.number().integer().min(1).default(1).notRequired(), limit: yup.number().integer().min(1).max(500).notRequired().default(250), diff --git a/web/scenes/Portal/Teams/TeamId/Apps/AppId/Transactions/page/server/getAccumulativeTransactionData.ts b/web/scenes/Portal/Teams/TeamId/Apps/AppId/Transactions/page/server/getAccumulativeTransactionData.ts index 4d8c86d79..29cd5f63f 100644 --- a/web/scenes/Portal/Teams/TeamId/Apps/AppId/Transactions/page/server/getAccumulativeTransactionData.ts +++ b/web/scenes/Portal/Teams/TeamId/Apps/AppId/Transactions/page/server/getAccumulativeTransactionData.ts @@ -8,11 +8,19 @@ export type GetAccumulativeTransactionDataReturnType = Awaited< ReturnType >; +const calculateUSDAmount = ( + tokenAmount: number, + tokenPrice: number, + tokenDecimals: number, +) => { + return (tokenAmount * tokenPrice) / 10 ** tokenDecimals; +}; + export const getAccumulativeTransactionData = async ( appId: string, ): Promise<{ accumulativeTransactions: PaymentMetadata[]; - accumulatedAmountUSD: number; + accumulatedTokenAmountUSD: number; }> => { try { if (!process.env.NEXT_SERVER_INTERNAL_PAYMENTS_ENDPOINT) { @@ -46,41 +54,61 @@ export const getAccumulativeTransactionData = async ( new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime(), ) as PaymentMetadata[]; - let accumulatedTokenAmount = 0; - const accumulativeTransactions = sortedTransactions.map((transaction) => { - // TODO - floating point issues here? test on real data - accumulatedTokenAmount += Number(transaction.inputTokenAmount); - - return { - ...transaction, - inputTokenAmount: String(accumulatedTokenAmount), - }; - }); - + // NOTE fetch all tokens, we need to know them before we can calculate the accumulated amount const tokenPriceResponse = (await ( await fetch( - `${process.env.NEXT_PUBLIC_PRICES_ENDPOINT}?cryptoCurrencies=WLD&fiatCurrencies=USD`, + `${process.env.NEXT_PUBLIC_PRICES_ENDPOINT}?cryptoCurrencies=WLD,USDCE&fiatCurrencies=USD`, ) ).json()) as { result: { - prices: { WLD: { USD: { amount: string; decimals: number } } }; + prices: { + WLD: { USD: { amount: string; decimals: number } }; + USDCE: { USD: { amount: string; decimals: number } }; + }; }; }; - const accumulatedAmountUSD = - (accumulatedTokenAmount * - Number(tokenPriceResponse.result.prices.WLD.USD.amount)) / - 10 ** tokenPriceResponse.result.prices.WLD.USD.decimals; + let accumulatedTokenAmountWLD = 0; + let accumulatedTokenAmountUSDCE = 0; + let accumulatedTokenAmountUSD = 0; + const cryptoCurrencies = new Set<"WLD" | "USDCE">(); + const accumulativeTransactions = sortedTransactions.map((transaction) => { + cryptoCurrencies.add(transaction.inputToken as "WLD" | "USDCE"); + + if (transaction.inputToken === "WLD") { + // TODO - floating point issues here? test on real data + accumulatedTokenAmountWLD += Number(transaction.inputTokenAmount); + + accumulatedTokenAmountUSD += calculateUSDAmount( + accumulatedTokenAmountWLD, + Number(tokenPriceResponse.result.prices.WLD.USD.amount), + tokenPriceResponse.result.prices.WLD.USD.decimals, + ); + } else if (transaction.inputToken === "USDCE") { + accumulatedTokenAmountUSDCE += Number(transaction.inputTokenAmount); + + accumulatedTokenAmountUSD += calculateUSDAmount( + accumulatedTokenAmountUSDCE, + Number(tokenPriceResponse!.result.prices.USDCE!.USD.amount), + tokenPriceResponse!.result.prices.USDCE!.USD.decimals, + ); + } + + return { + ...transaction, + inputTokenAmount: String(accumulatedTokenAmountUSD), + }; + }); return { accumulativeTransactions, - accumulatedAmountUSD, + accumulatedTokenAmountUSD, }; } catch (error) { logger.warn("Error fetching transaction data", { error }); return { accumulativeTransactions: [], - accumulatedAmountUSD: 0, + accumulatedTokenAmountUSD: 0, }; } }; diff --git a/web/scenes/Portal/Teams/TeamId/Apps/AppId/page/AppStatsGraph/GraphsSection/index.tsx b/web/scenes/Portal/Teams/TeamId/Apps/AppId/page/AppStatsGraph/GraphsSection/index.tsx index 2e4d9ce85..2faa7270d 100644 --- a/web/scenes/Portal/Teams/TeamId/Apps/AppId/page/AppStatsGraph/GraphsSection/index.tsx +++ b/web/scenes/Portal/Teams/TeamId/Apps/AppId/page/AppStatsGraph/GraphsSection/index.tsx @@ -6,6 +6,7 @@ import { Chart, ChartProps } from "@/components/Chart"; import { TYPOGRAPHY, Typography } from "@/components/Typography"; import { EngineType, TransactionStatus } from "@/lib/types"; import { ChartData, ChartOptions } from "chart.js"; +import clsx from "clsx"; import dayjs from "dayjs"; import tz from "dayjs/plugin/timezone"; import utc from "dayjs/plugin/utc"; @@ -83,8 +84,8 @@ export const GraphsSection = () => { [transactionsData?.accumulativeTransactions], ); const accumulatedTransactionAmountUSD = useMemo( - () => transactionsData?.accumulatedAmountUSD, - [transactionsData?.accumulatedAmountUSD], + () => transactionsData?.accumulatedTokenAmountUSD, + [transactionsData?.accumulatedTokenAmountUSD], ); const stats = useMemo( @@ -100,7 +101,6 @@ export const GraphsSection = () => { () => appStatsData?.app_stats_aggregate.aggregate?.sum?.unique_users ?? 0, [appStatsData?.app_stats_aggregate.aggregate?.sum?.unique_users], ); - console.log({ totalVerifications, totalUniqueUsers }); const engine = useMemo( () => appStatsData?.app?.[0]?.engine, @@ -171,7 +171,17 @@ export const GraphsSection = () => { )} {!appStatsLoading && !formattedVerificationsChartData && ( -
+
{engine === EngineType.OnChain ? "Analytics are not available for on-chain apps yet" @@ -189,7 +199,7 @@ export const GraphsSection = () => { )} {!appStatsLoading && formattedVerificationsChartData && (
-
+
{
)} {!appStatsLoading && formattedVerificationsChartData && ( -
-
+
+
{
)} {!transactionsLoading && !formattedTransactionsChartData && ( -
+
{engine === EngineType.OnChain ? "Analytics are not available for on-chain apps yet" diff --git a/web/scenes/Portal/Teams/TeamId/Apps/AppId/page/AppStatsGraph/StatCard/index.tsx b/web/scenes/Portal/Teams/TeamId/Apps/AppId/page/AppStatsGraph/StatCard/index.tsx index c3c990cb5..3c6b04618 100644 --- a/web/scenes/Portal/Teams/TeamId/Apps/AppId/page/AppStatsGraph/StatCard/index.tsx +++ b/web/scenes/Portal/Teams/TeamId/Apps/AppId/page/AppStatsGraph/StatCard/index.tsx @@ -6,6 +6,7 @@ export const StatCard = (props: { mainColorClassName: string; title: string; value: number | undefined | null; + isLoading: boolean; changePercentage?: number; }) => { return ( @@ -31,7 +32,8 @@ export const StatCard = (props: {
- {props.value?.toLocaleString() ?? } + {props.value?.toLocaleString() ?? + (props.isLoading ? : 0)}
diff --git a/web/scenes/Portal/Teams/TeamId/Apps/AppId/page/AppStatsGraph/StatCards/index.tsx b/web/scenes/Portal/Teams/TeamId/Apps/AppId/page/AppStatsGraph/StatCards/index.tsx index 449abbd78..d8f9cbb2d 100644 --- a/web/scenes/Portal/Teams/TeamId/Apps/AppId/page/AppStatsGraph/StatCards/index.tsx +++ b/web/scenes/Portal/Teams/TeamId/Apps/AppId/page/AppStatsGraph/StatCards/index.tsx @@ -36,7 +36,8 @@ export const StatCards = ({ appId }: { appId: string }) => { const [timespan] = useAtom(timespanAtom); const timespanValue = timespan.value; - const { metrics: appMetrics } = useGetMetrics(appId); + const { metrics: appMetrics, loading: isMetricsLoading } = + useGetMetrics(appId); const impressionsTotal = appMetrics?.impressions; const impressionsLast7Days = appMetrics?.impressions_7days; @@ -68,6 +69,7 @@ export const StatCards = ({ appId }: { appId: string }) => { timespanValue, })} changePercentage={impressionsPercentageChange} + isLoading={isMetricsLoading} /> { weekValue: newUsersLast7Days, timespanValue, })} + isLoading={isMetricsLoading} /> { weekValue: usersLast7Days, timespanValue, })} + isLoading={isMetricsLoading} /> { weekValue: uniqueUsersLast7Days, timespanValue, })} + isLoading={isMetricsLoading} /> -
-
); };