Files
supabase/apps/studio/data/auth/users-infinite-query.ts
Joshen LimandAlaister Young 536dc37d58 Chore/user management v2 panel part 2 (#29515)
* Set up banning and unbanning user, although untested due to API CORs issue

* Update search filter UI for users management V2

* Update API types

* Minoir

* Small fix

* Update UI

* Add support for resizing and re-ordering columns

* Add google profile image url to csp

* Revert test button

* Implement toggling of columns

* Fix loading

* Fully implement banning/unbanning user

* Fix

* Update apps/studio/components/interfaces/Auth/Users/UserOverview.tsx

Co-authored-by: Alaister Young <alaister@users.noreply.github.com>

* Fallback non CSP supported avatar urls to user icon

* Fix some bugs

* Remove prism-react-renderer from studio, add to ui patterns

* Migrate users query from API to studio

* Address some feedback

---------

Co-authored-by: Alaister Young <alaister@users.noreply.github.com>
2024-10-01 14:32:46 +08:00

111 lines
3.2 KiB
TypeScript

import { useInfiniteQuery, UseInfiniteQueryOptions } from '@tanstack/react-query'
import type { components } from 'data/api'
import { executeSql, ExecuteSqlError } from 'data/sql/execute-sql-query'
import { authKeys } from './keys'
export type Filter = 'verified' | 'unverified' | 'anonymous'
export type UsersVariables = {
projectRef?: string
connectionString?: string
page?: number
keywords?: string
filter?: Filter
providers?: string[]
sort?: 'created_at' | 'email' | 'phone'
order?: 'asc' | 'desc'
}
export const USERS_PAGE_LIMIT = 50
export type User = components['schemas']['UserBody']
const getUsersSQL = ({
page = 0,
verified,
keywords,
providers,
sort,
order,
}: {
page: number
verified?: Filter
keywords?: string
providers?: string[]
sort: string
order: string
}) => {
const offset = page * USERS_PAGE_LIMIT
const hasValidKeywords = keywords && keywords !== ''
const conditions: string[] = []
const baseQueryUsers = `select * from auth.users`
if (hasValidKeywords) {
conditions.push(
`id::text ilike '%${keywords}%' or email ilike '%${keywords}%' or phone ilike '%${keywords}%`
)
}
if (verified === 'verified') {
conditions.push(`email_confirmed_at IS NOT NULL or phone_confirmed_at IS NOT NULL`)
} else if (verified === 'anonymous') {
conditions.push(`is_anonymous is true`)
} else if (verified === 'unverified') {
conditions.push(`email_confirmed_at IS NULL AND phone_confirmed_at IS NULL`)
}
if (providers && providers.length > 0) {
conditions.push(
`(raw_app_meta_data->>'providers')::jsonb ?| array[${providers.map((p) => `'${p}'`).join(', ')}]`
)
}
const combinedConditions = conditions.map((x) => `(${x})`).join(' and ')
const sortOn = sort ?? 'created_at'
const sortOrder = order ?? 'desc'
return `${baseQueryUsers}${conditions.length > 0 ? ` where ${combinedConditions}` : ''} order by "${sortOn}" ${sortOrder} limit ${USERS_PAGE_LIMIT} offset ${offset};`
}
export type UsersData = { result: User[] }
export type UsersError = ExecuteSqlError
export const useUsersInfiniteQuery = <TData = UsersData>(
{ projectRef, connectionString, keywords, filter, providers, sort, order }: UsersVariables,
{ enabled = true, ...options }: UseInfiniteQueryOptions<UsersData, UsersError, TData> = {}
) =>
useInfiniteQuery<UsersData, UsersError, TData>(
authKeys.usersInfinite(projectRef, { keywords, filter, providers, sort, order }),
({ signal, pageParam }) => {
return executeSql(
{
projectRef,
connectionString,
sql: getUsersSQL({
page: pageParam,
verified: filter,
keywords,
providers,
sort: sort ?? 'created_at',
order: order ?? 'desc',
}),
queryKey: authKeys.usersInfinite(projectRef),
},
signal
)
},
{
staleTime: 0,
enabled: enabled && typeof projectRef !== 'undefined',
getNextPageParam(lastPage, pages) {
const page = pages.length
const hasNextPage = lastPage.result.length <= USERS_PAGE_LIMIT
if (!hasNextPage) return undefined
return page
},
...options,
}
)