import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { createFileRoute } from '@tanstack/react-router'; import { formatDistanceToNow } from 'date-fns'; import { CheckCircleIcon, Loader2Icon, XCircleIcon } from 'lucide-react'; import { useState } from 'react'; import { toast } from 'sonner'; import { Skeleton } from '@/components/skeleton'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { useAppParams } from '@/hooks/use-app-params'; import { useTRPC } from '@/integrations/trpc/react'; export const Route = createFileRoute( '/_app/$organizationId/$projectId/settings/_tabs/gsc' )({ component: GscSettings, }); function GscSettings() { const { projectId } = useAppParams(); const trpc = useTRPC(); const queryClient = useQueryClient(); const [selectedSite, setSelectedSite] = useState(''); const connectionQuery = useQuery( trpc.gsc.getConnection.queryOptions( { projectId }, { refetchInterval: 5000 } ) ); const sitesQuery = useQuery( trpc.gsc.getSites.queryOptions( { projectId }, { enabled: !!connectionQuery.data && !connectionQuery.data.siteUrl } ) ); const initiateOAuth = useMutation( trpc.gsc.initiateOAuth.mutationOptions({ onSuccess: (data) => { window.location.href = data.url; }, onError: () => { toast.error('Failed to initiate Google Search Console connection'); }, }) ); const selectSite = useMutation( trpc.gsc.selectSite.mutationOptions({ onSuccess: () => { toast.success('Site connected', { description: 'Backfill of 6 months of data has started.', }); queryClient.invalidateQueries(trpc.gsc.getConnection.pathFilter()); }, onError: () => { toast.error('Failed to select site'); }, }) ); const disconnect = useMutation( trpc.gsc.disconnect.mutationOptions({ onSuccess: () => { toast.success('Disconnected from Google Search Console'); queryClient.invalidateQueries(trpc.gsc.getConnection.pathFilter()); }, onError: () => { toast.error('Failed to disconnect'); }, }) ); const connection = connectionQuery.data; if (connectionQuery.isLoading) { return (
); } // Not connected at all if (!connection) { return (

Google Search Console

Connect your Google Search Console property to import search performance data.

You will be redirected to Google to authorize access. Only read-only access to Search Console data is requested.

); } // Connected but no site selected yet if (!connection.siteUrl) { const sites = sitesQuery.data ?? []; return (

Select a property

Choose which Google Search Console property to connect to this project.

{sitesQuery.isLoading ? ( ) : sites.length === 0 ? (

No Search Console properties found for this Google account.

) : ( <> )}
); } // Token expired — show reconnect prompt if (connection.lastSyncStatus === 'token_expired') { return (

Google Search Console

Connected to Google Search Console.

Authorization expired

Your Google Search Console authorization has expired or been revoked. Please reconnect to continue syncing data.

{connection.lastSyncError && (

{connection.lastSyncError}

)}
); } // Fully connected const syncStatusIcon = connection.lastSyncStatus === 'success' ? ( ) : connection.lastSyncStatus === 'error' ? ( ) : null; const syncStatusVariant = connection.lastSyncStatus === 'success' ? 'success' : connection.lastSyncStatus === 'error' ? 'destructive' : 'secondary'; return (

Google Search Console

Connected to Google Search Console.

Property
{connection.siteUrl}
{connection.backfillStatus && (
Backfill
{connection.backfillStatus === 'running' && ( )} {connection.backfillStatus}
)} {connection.lastSyncedAt && (
Last synced
{connection.lastSyncStatus && ( {syncStatusIcon} {connection.lastSyncStatus} )} {formatDistanceToNow(new Date(connection.lastSyncedAt), { addSuffix: true, })}
)} {connection.lastSyncError && (
Last error
{connection.lastSyncError}
)}
); }