import * as React from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { toast } from "sonner"; import { Ga4PropertyPicker, type Ga4PropertySelection, } from "@/client/features/ga4/Ga4PropertyPicker"; import { GoogleGlyph } from "@/client/features/gsc/GoogleGlyph"; import { GoogleLinkErrorAlert } from "@/client/features/integrations/GoogleLinkErrorAlert"; import { GoogleOAuthSetupWarning } from "@/client/features/integrations/GoogleOAuthSetupWarning"; import { IntegrationConnectionCard } from "@/client/features/integrations/IntegrationConnectionCard"; import { GoogleAnalyticsLogo } from "@/client/features/integrations/GoogleProductLogos"; import { startGoogleLink } from "@/client/features/integrations/startGoogleLink"; import { getStandardErrorMessage } from "@/client/lib/error-messages"; import { captureClientEvent } from "@/client/lib/posthog"; import { isHostedClientAuthMode } from "@/lib/auth-mode"; import { disconnectGa4, getGa4Connection, listGa4Properties, setGa4Property, } from "@/serverFunctions/ga4"; import { GA4_SELF_HOSTED_SETUP_DOCS_URL } from "@/shared/ga4"; export function GoogleAnalyticsConnectionCard({ projectId, onDismiss, dismissing = false, heading, }: { projectId: string; onDismiss?: () => void; dismissing?: boolean; heading?: React.ReactNode; }) { const hosted = isHostedClientAuthMode(); const queryClient = useQueryClient(); const [picking, setPicking] = React.useState(false); const [selection, setSelection] = React.useState( null, ); const connectionKey = ["ga4Connection", projectId]; const connectionQuery = useQuery({ queryKey: connectionKey, queryFn: () => getGa4Connection({ data: { projectId } }), }); const connection = connectionQuery.data; const connected = Boolean(connection?.connected); const selfHostedNeedsSetup = !hosted && connectionQuery.isSuccess && !connection?.googleOAuthConfigured; const showPicker = picking || (connection?.currentUserHasGrant && !connected); const propertiesQuery = useQuery({ queryKey: ["ga4Properties", projectId], queryFn: () => listGa4Properties({ data: { projectId } }), enabled: Boolean(showPicker && !selfHostedNeedsSetup), }); const accounts = React.useMemo( () => propertiesQuery.data?.accounts ?? [], [propertiesQuery.data?.accounts], ); React.useEffect(() => { if (selection) return; for (const account of accounts) { const selectedProperty = account.properties.find( (property) => property.isSelected, ); if (selectedProperty) { setSelection({ accountId: account.accountId, propertyId: selectedProperty.propertyId, }); return; } } }, [accounts, selection]); const invalidateConnectionState = () => { void queryClient.invalidateQueries({ queryKey: connectionKey }); void queryClient.invalidateQueries({ queryKey: ["dashboardActivation", projectId], }); void queryClient.invalidateQueries({ queryKey: ["dashboardGa4Report", projectId], }); }; const setPropertyMutation = useMutation({ mutationFn: (selected: Ga4PropertySelection) => setGa4Property({ data: { projectId, ...selected } }), onSuccess: () => { captureClientEvent("ga4:property_select"); toast.success("Google Analytics connected"); setPicking(false); invalidateConnectionState(); }, onError: (error) => toast.error(getStandardErrorMessage(error)), }); const disconnectMutation = useMutation({ mutationFn: () => disconnectGa4({ data: { projectId } }), onSuccess: () => { toast.success("Google Analytics disconnected"); setPicking(false); setSelection(null); invalidateConnectionState(); }, onError: (error) => toast.error(getStandardErrorMessage(error)), }); const handleConnect = () => void startGoogleLink("ga4", window.location.href); return ( <> {heading} } status={ connectionQuery.isLoading ? undefined : selfHostedNeedsSetup ? "setup_required" : connected ? "connected" : "disconnected" } > {connectionQuery.isLoading ? (
Checking…
) : selfHostedNeedsSetup ? (
{onDismiss ? ( ) : null}
) : connected && !picking ? ( { setSelection(null); setPicking(true); }} onDisconnect={() => disconnectMutation.mutate()} disconnecting={disconnectMutation.isPending} /> ) : showPicker ? ( selection && setPropertyMutation.mutate(selection)} saving={setPropertyMutation.isPending} onRetry={() => void propertiesQuery.refetch()} secondaryAction={ connected ? { label: "Cancel", onClick: () => setPicking(false) } : onDismiss ? { label: "Dismiss", disabled: dismissing, onClick: onDismiss, } : { label: "Disconnect", destructive: true, disabled: disconnectMutation.isPending, onClick: () => disconnectMutation.mutate(), } } /> ) : (

Connect GA4 to understand what organic visitors do after they land on your site.

{onDismiss ? ( ) : null}
)}
); } function DismissButton({ onClick, disabled, }: { onClick: () => void; disabled: boolean; }) { return ( ); } function ConnectedState({ displayName, propertyId, timeZone, currencyCode, connectedByEmail, onChange, onDisconnect, disconnecting, }: { displayName: string; propertyId: string; timeZone: string; currencyCode: string; connectedByEmail: string | null; onChange: () => void; onDisconnect: () => void; disconnecting: boolean; }) { const numericPropertyId = propertyId.replace(/^properties\//, ""); return (

Selected property

{displayName}

ID {numericPropertyId}
Time zone
{timeZone}
Currency
{currencyCode}
{connectedByEmail ? (
Connected account
{connectedByEmail}
) : null}
); }