import { keepPreviousData } from '@tanstack/react-query' import { useMemo } from 'react' import { CRON_JOBS_THRESHOLD } from './CronJobsTab.constants' import type { ConnectionVars } from '@/data/common.types' import { useCronJobsCountEstimateQuery } from '@/data/database-cron-jobs/database-cron-jobs-count-estimate-query' import { useCronJobsCountQuery } from '@/data/database-cron-jobs/database-cron-jobs-count-query' import { CronJob, useCronJobsInfiniteQuery, } from '@/data/database-cron-jobs/database-cron-jobs-infinite-query' import { useCronJobsMinimalInfiniteQuery } from '@/data/database-cron-jobs/database-cron-jobs-minimal-infinite-query' import { COST_THRESHOLD_ERROR } from '@/data/sql/execute-sql-query' import type { ResponseError } from '@/types' // ============================================================================= // Input types // ============================================================================= type UseCronJobsDataParams = ConnectionVars & { searchQuery: string | null } // ============================================================================= // Shared state types (available in all states) // ============================================================================= interface CronJobsGridState { rows: Array queryCost?: number isSuccess: boolean isLoading: boolean error: ResponseError | null isRefetching: boolean isFetchingNextPage: boolean hasNextPage: boolean isMinimal: boolean refetch: () => void fetchNextPage: () => void } interface CronJobsCountState { value: number | undefined isEstimate: boolean isLoading: boolean } // ============================================================================= // Result type // ============================================================================= interface UseCronJobsDataResult { /** State for the cron jobs grid */ grid: CronJobsGridState /** State for the cron jobs count (exact or estimate) */ count: CronJobsCountState } export function useCronJobsData({ projectRef, connectionString, searchQuery, }: UseCronJobsDataParams): UseCronJobsDataResult { const isProjectReady = !!projectRef const { data: cronJobsData, error: cronJobsError, isSuccess: isCronJobsSuccess, isLoading: isCronJobsLoading, isRefetching: isCronJobsRefetching, isFetchingNextPage, hasNextPage = false, refetch: refetchCronJobs, fetchNextPage, } = useCronJobsInfiniteQuery( { projectRef, connectionString, searchTerm: searchQuery ?? undefined }, { placeholderData: Boolean(searchQuery) ? keepPreviousData : undefined, staleTime: Infinity, } ) const useMinimalQuery = cronJobsError?.message === COST_THRESHOLD_ERROR const { data: cronJobsMinimalData, error: cronJobsMinimalError, isSuccess: isCronJobsMinimalSuccess, isLoading: isCronJobsMinimalLoading, isRefetching: isCronJobsMinimalRefetching, isFetchingNextPage: isFetchingNextPageMinimal, hasNextPage: hasNextPageMinimal = false, refetch: refetchCronJobsMinimal, fetchNextPage: fetchNextPageMinimal, } = useCronJobsMinimalInfiniteQuery( { projectRef, connectionString, searchTerm: searchQuery ?? undefined }, { placeholderData: Boolean(searchQuery) ? keepPreviousData : undefined, staleTime: Infinity, enabled: useMinimalQuery, } ) const cronJobs = useMemo(() => { if (useMinimalQuery) { return cronJobsMinimalData?.pages.flatMap((page) => page) ?? [] } else { return cronJobsData?.pages.flatMap((page) => page) ?? [] } }, [useMinimalQuery, cronJobsData?.pages, cronJobsMinimalData?.pages]) // Fetch count - gated on cron.job table size // Always fetch the estimate first (it's fast since it uses pg_stat) const { data: estimatedCount, isPending: isLoadingEstimatedCount, isError: isEstimatedCountError, } = useCronJobsCountEstimateQuery({ projectRef, connectionString }, { enabled: isProjectReady }) const hasLargeCronJobsTable = typeof estimatedCount === 'number' && estimatedCount > CRON_JOBS_THRESHOLD // Exact count is enabled when the cron.job table is small enough const isCountQueryEnabled = isProjectReady && !isLoadingEstimatedCount && !isEstimatedCountError && !hasLargeCronJobsTable const { data: exactCount, isPending: isLoadingExactCount } = useCronJobsCountQuery( { projectRef, connectionString }, { enabled: isCountQueryEnabled } ) return { grid: { rows: cronJobs, queryCost: cronJobsError?.metadata?.cost, error: useMinimalQuery ? cronJobsMinimalError : cronJobsError, isSuccess: useMinimalQuery ? isCronJobsMinimalSuccess : isCronJobsSuccess, isLoading: useMinimalQuery ? isCronJobsMinimalLoading : isCronJobsLoading, isRefetching: useMinimalQuery ? isCronJobsMinimalRefetching : isCronJobsRefetching, isFetchingNextPage: useMinimalQuery ? isFetchingNextPageMinimal : isFetchingNextPage, hasNextPage: useMinimalQuery ? hasNextPageMinimal : hasNextPage, isMinimal: useMinimalQuery, refetch: useMinimalQuery ? refetchCronJobsMinimal : refetchCronJobs, fetchNextPage: useMinimalQuery ? fetchNextPageMinimal : fetchNextPage, }, count: { value: isCountQueryEnabled ? exactCount : estimatedCount, isEstimate: !isCountQueryEnabled, isLoading: isCountQueryEnabled ? isLoadingExactCount : isLoadingEstimatedCount, }, } }