mirror of
https://github.com/supabase/supabase.git
synced 2026-10-08 10:55:06 +03:00
## Context
We reached to a consensus that we'll keep manually written queries
within the `pg-meta` package
I'll shift queries over in several PRs but wanted to double check if the
changes here is a good folder structure
## Changes involved
- Am organizing dashboard queries in `pg-meta` based on product
- So folder structure will be `/sql/studio/[product]/*.ts`
- Every constant will be exported directly from `pg-meta`'s index
- So consumption in dashboard will just be `import { xxx } from
'@supabase/pg-meta`
cc @avallete and @soedirgo on this one - lemme know if this is alright,
or if there's a preferred way to do this
116 lines
3.4 KiB
TypeScript
116 lines
3.4 KiB
TypeScript
import { getPaginatedUsersSQL, OptimizedSearchColumns, UsersCursor } from '@supabase/pg-meta'
|
|
import { InfiniteData, useInfiniteQuery } from '@tanstack/react-query'
|
|
import type { components } from 'data/api'
|
|
import { executeSql, ExecuteSqlError } from 'data/sql/execute-sql-query'
|
|
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
|
import { PROJECT_STATUS } from 'lib/constants'
|
|
import { UseCustomInfiniteQueryOptions } from 'types'
|
|
|
|
import { authKeys } from './keys'
|
|
|
|
const USERS_PAGE_LIMIT = 50
|
|
type UsersData = { result: User[] }
|
|
type UsersError = ExecuteSqlError
|
|
type UsersVariables = {
|
|
projectRef?: string
|
|
connectionString?: string | null
|
|
page?: number
|
|
keywords?: string
|
|
filter?: Filter
|
|
providers?: string[]
|
|
sort?: 'id' | 'created_at' | 'email' | 'phone' | 'last_sign_in_at'
|
|
order?: 'asc' | 'desc'
|
|
/** If set, uses optimized prefix search for the specified column */
|
|
column?: OptimizedSearchColumns
|
|
startAt?: string
|
|
|
|
improvedSearchEnabled?: boolean
|
|
}
|
|
|
|
export type Filter = 'verified' | 'unverified' | 'anonymous'
|
|
export type User = components['schemas']['UserBody'] & { providers: readonly string[] }
|
|
|
|
export const useUsersInfiniteQuery = <TData = UsersData>(
|
|
{
|
|
projectRef,
|
|
connectionString,
|
|
keywords,
|
|
filter,
|
|
providers,
|
|
sort,
|
|
order,
|
|
column,
|
|
|
|
improvedSearchEnabled = false,
|
|
}: UsersVariables,
|
|
{
|
|
enabled = true,
|
|
...options
|
|
}: UseCustomInfiniteQueryOptions<
|
|
UsersData,
|
|
UsersError,
|
|
InfiniteData<TData>,
|
|
readonly unknown[],
|
|
string | number | UsersCursor | undefined
|
|
> = {}
|
|
) => {
|
|
const { data: project } = useSelectedProjectQuery()
|
|
const isActive = project?.status === PROJECT_STATUS.ACTIVE_HEALTHY
|
|
|
|
return useInfiniteQuery({
|
|
queryKey: authKeys.usersInfinite(projectRef, {
|
|
keywords,
|
|
filter,
|
|
providers,
|
|
sort,
|
|
order,
|
|
column,
|
|
}),
|
|
queryFn: ({ signal, pageParam }) => {
|
|
return executeSql(
|
|
{
|
|
projectRef,
|
|
connectionString,
|
|
sql: getPaginatedUsersSQL({
|
|
page: column ? undefined : (pageParam as number),
|
|
verified: filter,
|
|
keywords,
|
|
providers,
|
|
sort: sort ?? 'id',
|
|
order: order ?? 'asc',
|
|
limit: USERS_PAGE_LIMIT,
|
|
column,
|
|
startAt: column ? (pageParam as string) : undefined,
|
|
cursor: improvedSearchEnabled ? (pageParam as UsersCursor) : undefined,
|
|
improvedSearchEnabled,
|
|
}),
|
|
},
|
|
signal
|
|
)
|
|
},
|
|
enabled: enabled && typeof projectRef !== 'undefined' && isActive,
|
|
initialPageParam: undefined,
|
|
getNextPageParam(lastPage, pages) {
|
|
const hasNextPage = lastPage.result.length >= USERS_PAGE_LIMIT
|
|
if (!hasNextPage) return undefined
|
|
|
|
const lastItem = lastPage.result[lastPage.result.length - 1]
|
|
if (!lastItem) return undefined
|
|
|
|
// for improved search, we always use cursor-based pagination where the ORDER BY
|
|
// clause is the specified sort column + the id column as a tie breaker
|
|
if (improvedSearchEnabled) {
|
|
const sortColumn = sort ?? 'created_at'
|
|
return { sort: lastItem[sortColumn], id: lastItem.id } as UsersCursor
|
|
}
|
|
|
|
if (column) {
|
|
return lastItem[column as Exclude<OptimizedSearchColumns, 'name'>]
|
|
} else {
|
|
return pages.length
|
|
}
|
|
},
|
|
...options,
|
|
})
|
|
}
|