From a12170e4c9ece88ff7703b36e0794598be74f515 Mon Sep 17 00:00:00 2001 From: Abhay Mishra Date: Mon, 15 Dec 2025 22:07:52 +0530 Subject: [PATCH] refactor(frontend): Extracted useQuery and useMutation from the main branch (#12031) Co-authored-by: sp.wack <83104063+amanape@users.noreply.github.com> --- .../features/payment/setup-payment-modal.tsx | 14 +---- .../features/settings/api-keys-manager.tsx | 6 +-- frontend/src/hooks/mutation/use-accept-tos.ts | 54 +++++++++++++++++++ .../mutation/use-create-billing-session.ts | 19 +++++++ .../hooks/mutation/use-refresh-llm-api-key.ts | 23 ++++++++ frontend/src/hooks/query/use-llm-api-key.ts | 19 +------ frontend/src/routes/accept-tos.tsx | 47 ++-------------- 7 files changed, 105 insertions(+), 77 deletions(-) create mode 100644 frontend/src/hooks/mutation/use-accept-tos.ts create mode 100644 frontend/src/hooks/mutation/use-create-billing-session.ts create mode 100644 frontend/src/hooks/mutation/use-refresh-llm-api-key.ts diff --git a/frontend/src/components/features/payment/setup-payment-modal.tsx b/frontend/src/components/features/payment/setup-payment-modal.tsx index 30cb0a4e54..7d8883a719 100644 --- a/frontend/src/components/features/payment/setup-payment-modal.tsx +++ b/frontend/src/components/features/payment/setup-payment-modal.tsx @@ -1,24 +1,14 @@ -import { useMutation } from "@tanstack/react-query"; import { Trans, useTranslation } from "react-i18next"; import { I18nKey } from "#/i18n/declaration"; import OpenHandsLogo from "#/assets/branding/openhands-logo.svg?react"; import { ModalBackdrop } from "#/components/shared/modals/modal-backdrop"; import { ModalBody } from "#/components/shared/modals/modal-body"; -import BillingService from "#/api/billing-service/billing-service.api"; import { BrandButton } from "../settings/brand-button"; -import { displayErrorToast } from "#/utils/custom-toast-handlers"; +import { useCreateBillingSession } from "#/hooks/mutation/use-create-billing-session"; export function SetupPaymentModal() { const { t } = useTranslation(); - const { mutate, isPending } = useMutation({ - mutationFn: BillingService.createBillingSessionResponse, - onSuccess: (data) => { - window.location.href = data; - }, - onError: () => { - displayErrorToast(t(I18nKey.BILLING$ERROR_WHILE_CREATING_SESSION)); - }, - }); + const { mutate, isPending } = useCreateBillingSession(); return ( diff --git a/frontend/src/components/features/settings/api-keys-manager.tsx b/frontend/src/components/features/settings/api-keys-manager.tsx index 82d86fb4a9..20a8807aa0 100644 --- a/frontend/src/components/features/settings/api-keys-manager.tsx +++ b/frontend/src/components/features/settings/api-keys-manager.tsx @@ -13,10 +13,8 @@ import { CreateApiKeyModal } from "./create-api-key-modal"; import { DeleteApiKeyModal } from "./delete-api-key-modal"; import { NewApiKeyModal } from "./new-api-key-modal"; import { useApiKeys } from "#/hooks/query/use-api-keys"; -import { - useLlmApiKey, - useRefreshLlmApiKey, -} from "#/hooks/query/use-llm-api-key"; +import { useLlmApiKey } from "#/hooks/query/use-llm-api-key"; +import { useRefreshLlmApiKey } from "#/hooks/mutation/use-refresh-llm-api-key"; interface LlmApiKeyManagerProps { llmApiKey: { key: string | null } | undefined; diff --git a/frontend/src/hooks/mutation/use-accept-tos.ts b/frontend/src/hooks/mutation/use-accept-tos.ts new file mode 100644 index 0000000000..a159b1458c --- /dev/null +++ b/frontend/src/hooks/mutation/use-accept-tos.ts @@ -0,0 +1,54 @@ +import { useMutation } from "@tanstack/react-query"; +import { usePostHog } from "posthog-js/react"; +import { useNavigate } from "react-router"; +import { openHands } from "#/api/open-hands-axios"; +import { handleCaptureConsent } from "#/utils/handle-capture-consent"; +import { useTracking } from "#/hooks/use-tracking"; + +interface AcceptTosVariables { + redirectUrl: string; +} + +interface AcceptTosResponse { + redirect_url?: string; +} + +export const useAcceptTos = () => { + const posthog = usePostHog(); + const navigate = useNavigate(); + const { trackUserSignupCompleted } = useTracking(); + + return useMutation({ + mutationFn: async ({ redirectUrl }: AcceptTosVariables) => { + // Set consent for analytics + handleCaptureConsent(posthog, true); + + // Call the API to record TOS acceptance in the database + return openHands.post("/api/accept_tos", { + redirect_url: redirectUrl, + }); + }, + onSuccess: (response, { redirectUrl }) => { + // Track user signup completion + trackUserSignupCompleted(); + + // Get the redirect URL from the response + const finalRedirectUrl = response.data.redirect_url || redirectUrl; + + // Check if the redirect URL is an external URL (starts with http or https) + if ( + finalRedirectUrl.startsWith("http://") || + finalRedirectUrl.startsWith("https://") + ) { + // For external URLs, redirect using window.location + window.location.href = finalRedirectUrl; + } else { + // For internal routes, use navigate + navigate(finalRedirectUrl); + } + }, + onError: () => { + window.location.href = "/"; + }, + }); +}; diff --git a/frontend/src/hooks/mutation/use-create-billing-session.ts b/frontend/src/hooks/mutation/use-create-billing-session.ts new file mode 100644 index 0000000000..f8f0716cb2 --- /dev/null +++ b/frontend/src/hooks/mutation/use-create-billing-session.ts @@ -0,0 +1,19 @@ +import { useMutation } from "@tanstack/react-query"; +import { useTranslation } from "react-i18next"; +import { I18nKey } from "#/i18n/declaration"; +import BillingService from "#/api/billing-service/billing-service.api"; +import { displayErrorToast } from "#/utils/custom-toast-handlers"; + +export const useCreateBillingSession = () => { + const { t } = useTranslation(); + + return useMutation({ + mutationFn: BillingService.createBillingSessionResponse, + onSuccess: (data) => { + window.location.href = data; + }, + onError: () => { + displayErrorToast(t(I18nKey.BILLING$ERROR_WHILE_CREATING_SESSION)); + }, + }); +}; diff --git a/frontend/src/hooks/mutation/use-refresh-llm-api-key.ts b/frontend/src/hooks/mutation/use-refresh-llm-api-key.ts new file mode 100644 index 0000000000..11a112e182 --- /dev/null +++ b/frontend/src/hooks/mutation/use-refresh-llm-api-key.ts @@ -0,0 +1,23 @@ +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { openHands } from "#/api/open-hands-axios"; +import { + LLM_API_KEY_QUERY_KEY, + LlmApiKeyResponse, +} from "#/hooks/query/use-llm-api-key"; + +export function useRefreshLlmApiKey() { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async () => { + const { data } = await openHands.post( + "/api/keys/llm/byor/refresh", + ); + return data; + }, + onSuccess: () => { + // Invalidate the LLM API key query to trigger a refetch + queryClient.invalidateQueries({ queryKey: [LLM_API_KEY_QUERY_KEY] }); + }, + }); +} diff --git a/frontend/src/hooks/query/use-llm-api-key.ts b/frontend/src/hooks/query/use-llm-api-key.ts index 5dcea9f714..58dee11411 100644 --- a/frontend/src/hooks/query/use-llm-api-key.ts +++ b/frontend/src/hooks/query/use-llm-api-key.ts @@ -1,4 +1,4 @@ -import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { useQuery } from "@tanstack/react-query"; import { openHands } from "#/api/open-hands-axios"; import { useConfig } from "./use-config"; @@ -23,20 +23,3 @@ export function useLlmApiKey() { gcTime: 1000 * 60 * 15, // 15 minutes }); } - -export function useRefreshLlmApiKey() { - const queryClient = useQueryClient(); - - return useMutation({ - mutationFn: async () => { - const { data } = await openHands.post( - "/api/keys/llm/byor/refresh", - ); - return data; - }, - onSuccess: () => { - // Invalidate the LLM API key query to trigger a refetch - queryClient.invalidateQueries({ queryKey: [LLM_API_KEY_QUERY_KEY] }); - }, - }); -} diff --git a/frontend/src/routes/accept-tos.tsx b/frontend/src/routes/accept-tos.tsx index f723f2a5f6..a3732273e3 100644 --- a/frontend/src/routes/accept-tos.tsx +++ b/frontend/src/routes/accept-tos.tsx @@ -1,66 +1,27 @@ import React from "react"; import { useTranslation } from "react-i18next"; -import { useNavigate, useSearchParams } from "react-router"; -import { useMutation } from "@tanstack/react-query"; -import { usePostHog } from "posthog-js/react"; +import { useSearchParams } from "react-router"; import { I18nKey } from "#/i18n/declaration"; import OpenHandsLogo from "#/assets/branding/openhands-logo.svg?react"; import { TOSCheckbox } from "#/components/features/waitlist/tos-checkbox"; import { BrandButton } from "#/components/features/settings/brand-button"; -import { handleCaptureConsent } from "#/utils/handle-capture-consent"; -import { openHands } from "#/api/open-hands-axios"; import { ModalBackdrop } from "#/components/shared/modals/modal-backdrop"; -import { useTracking } from "#/hooks/use-tracking"; +import { useAcceptTos } from "#/hooks/mutation/use-accept-tos"; export default function AcceptTOS() { - const posthog = usePostHog(); const { t } = useTranslation(); - const navigate = useNavigate(); const [searchParams] = useSearchParams(); const [isTosAccepted, setIsTosAccepted] = React.useState(false); - const { trackUserSignupCompleted } = useTracking(); // Get the redirect URL from the query parameters const redirectUrl = searchParams.get("redirect_url") || "/"; // Use mutation for accepting TOS - const { mutate: acceptTOS, isPending: isSubmitting } = useMutation({ - mutationFn: async () => { - // Set consent for analytics - handleCaptureConsent(posthog, true); - - // Call the API to record TOS acceptance in the database - return openHands.post("/api/accept_tos", { - redirect_url: redirectUrl, - }); - }, - onSuccess: (response) => { - // Track user signup completion - trackUserSignupCompleted(); - - // Get the redirect URL from the response - const finalRedirectUrl = response.data.redirect_url || redirectUrl; - - // Check if the redirect URL is an external URL (starts with http or https) - if ( - finalRedirectUrl.startsWith("http://") || - finalRedirectUrl.startsWith("https://") - ) { - // For external URLs, redirect using window.location - window.location.href = finalRedirectUrl; - } else { - // For internal routes, use navigate - navigate(finalRedirectUrl); - } - }, - onError: () => { - window.location.href = "/"; - }, - }); + const { mutate: acceptTOS, isPending: isSubmitting } = useAcceptTos(); const handleAcceptTOS = () => { if (isTosAccepted && !isSubmitting) { - acceptTOS(); + acceptTOS({ redirectUrl }); } };