fix search api: wrap body in search key, use X-Per-Page header, 50 per page

This commit is contained in:
Justin Edmund 2025-12-02 15:06:38 -08:00
parent 7808c75452
commit 491026399f
2 changed files with 58 additions and 13 deletions

View file

@ -229,12 +229,15 @@ export class SearchAdapter extends BaseAdapter {
})
// Search endpoints don't use credentials to avoid CORS
// Rails expects params nested under 'search' key
// Per-page is sent via X-Per-Page header
return this.request<SearchResponse>('/search/all', {
method: 'POST',
body: JSON.stringify(body),
body: { search: body },
credentials: 'omit',
// Cache search results for 5 minutes by default
cacheTTL: params.query ? 300000 : 0 // Don't cache empty searches
cacheTTL: params.query ? 300000 : 0, // Don't cache empty searches
headers: params.per ? { 'X-Per-Page': String(params.per) } : undefined
})
}
@ -254,11 +257,14 @@ export class SearchAdapter extends BaseAdapter {
extra: true
})
// Rails expects params nested under 'search' key
// Per-page is sent via X-Per-Page header
return this.request<SearchResponse>('/search/weapons', {
method: 'POST',
body: JSON.stringify(body),
body: { search: body },
credentials: 'omit',
cacheTTL: params.query ? 300000 : 0
cacheTTL: params.query ? 300000 : 0,
headers: params.per ? { 'X-Per-Page': String(params.per) } : undefined
})
}
@ -279,11 +285,14 @@ export class SearchAdapter extends BaseAdapter {
gachaAvailable: true
})
// Rails expects params nested under 'search' key
// Per-page is sent via X-Per-Page header
return this.request<SearchResponse>('/search/characters', {
method: 'POST',
body: JSON.stringify(body),
body: { search: body },
credentials: 'omit',
cacheTTL: params.query ? 300000 : 0
cacheTTL: params.query ? 300000 : 0,
headers: params.per ? { 'X-Per-Page': String(params.per) } : undefined
})
}
@ -301,11 +310,14 @@ export class SearchAdapter extends BaseAdapter {
subaura: true
})
// Rails expects params nested under 'search' key
// Per-page is sent via X-Per-Page header
return this.request<SearchResponse>('/search/summons', {
method: 'POST',
body: JSON.stringify(body),
body: { search: body },
credentials: 'omit',
cacheTTL: params.query ? 300000 : 0
cacheTTL: params.query ? 300000 : 0,
headers: params.per ? { 'X-Per-Page': String(params.per) } : undefined
})
}

View file

@ -23,6 +23,10 @@ export interface SearchFilters {
proficiency2?: number[]
subaura?: boolean
extra?: boolean
// Character-specific filters
season?: number[]
characterSeries?: number[]
gachaAvailable?: boolean
}
/**
@ -44,6 +48,9 @@ export interface SearchPageResult {
totalPages: number
}
/** Default number of results per page for search queries */
const SEARCH_PER_PAGE = 50
/**
* Builds search parameters from query string and filters
*/
@ -51,11 +58,13 @@ function buildSearchParams(
query: string,
filters: SearchFilters | undefined,
page: number,
locale: 'en' | 'ja' = 'en'
locale: 'en' | 'ja' = 'en',
exclude?: string[]
): SearchParams {
const params: SearchParams = {
page,
locale
locale,
per: SEARCH_PER_PAGE
}
// Only include query if not empty
@ -63,6 +72,11 @@ function buildSearchParams(
params.query = query.trim()
}
// Only include exclude if provided
if (exclude && exclude.length > 0) {
params.exclude = exclude
}
// Build filters object with only defined values
if (filters) {
const apiFilters: NonNullable<SearchParams['filters']> = {}
@ -85,6 +99,16 @@ function buildSearchParams(
if (filters.extra !== undefined) {
apiFilters.extra = filters.extra
}
// Character-specific filters
if (filters.season && filters.season.length > 0) {
apiFilters.season = filters.season
}
if (filters.characterSeries && filters.characterSeries.length > 0) {
apiFilters.characterSeries = filters.characterSeries
}
if (filters.gachaAvailable !== undefined) {
apiFilters.gachaAvailable = filters.gachaAvailable
}
// Only include filters if any were set
if (Object.keys(apiFilters).length > 0) {
@ -154,13 +178,21 @@ export const searchQueries = {
* @param query - Search query string
* @param filters - Optional filter configuration
* @param locale - Locale for results (default: 'en')
* @param exclude - Optional array of character IDs to exclude from results
* @param enabled - Whether the query should be enabled (default: true)
* @returns Infinite query options for character search
*/
characters: (query: string = '', filters?: SearchFilters, locale: 'en' | 'ja' = 'en') =>
characters: (
query: string = '',
filters?: SearchFilters,
locale: 'en' | 'ja' = 'en',
exclude?: string[],
enabled: boolean = true
) =>
infiniteQueryOptions({
queryKey: ['search', 'characters', query, filters, locale] as const,
queryKey: ['search', 'characters', query, filters, locale, exclude] as const,
queryFn: async ({ pageParam }): Promise<SearchPageResult> => {
const params = buildSearchParams(query, filters, pageParam, locale)
const params = buildSearchParams(query, filters, pageParam, locale, exclude)
const response = await searchAdapter.searchCharacters(params)
return {
@ -178,6 +210,7 @@ export const searchQueries = {
},
staleTime: 1000 * 60 * 5, // 5 minutes
gcTime: 1000 * 60 * 30, // 30 minutes
enabled
}),
/**