fix search api: wrap body in search key, use X-Per-Page header, 50 per page
This commit is contained in:
parent
7808c75452
commit
491026399f
2 changed files with 58 additions and 13 deletions
|
|
@ -229,12 +229,15 @@ export class SearchAdapter extends BaseAdapter {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Search endpoints don't use credentials to avoid CORS
|
// 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', {
|
return this.request<SearchResponse>('/search/all', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify(body),
|
body: { search: body },
|
||||||
credentials: 'omit',
|
credentials: 'omit',
|
||||||
// Cache search results for 5 minutes by default
|
// 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
|
extra: true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Rails expects params nested under 'search' key
|
||||||
|
// Per-page is sent via X-Per-Page header
|
||||||
return this.request<SearchResponse>('/search/weapons', {
|
return this.request<SearchResponse>('/search/weapons', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify(body),
|
body: { search: body },
|
||||||
credentials: 'omit',
|
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
|
gachaAvailable: true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Rails expects params nested under 'search' key
|
||||||
|
// Per-page is sent via X-Per-Page header
|
||||||
return this.request<SearchResponse>('/search/characters', {
|
return this.request<SearchResponse>('/search/characters', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify(body),
|
body: { search: body },
|
||||||
credentials: 'omit',
|
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
|
subaura: true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Rails expects params nested under 'search' key
|
||||||
|
// Per-page is sent via X-Per-Page header
|
||||||
return this.request<SearchResponse>('/search/summons', {
|
return this.request<SearchResponse>('/search/summons', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify(body),
|
body: { search: body },
|
||||||
credentials: 'omit',
|
credentials: 'omit',
|
||||||
cacheTTL: params.query ? 300000 : 0
|
cacheTTL: params.query ? 300000 : 0,
|
||||||
|
headers: params.per ? { 'X-Per-Page': String(params.per) } : undefined
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,10 @@ export interface SearchFilters {
|
||||||
proficiency2?: number[]
|
proficiency2?: number[]
|
||||||
subaura?: boolean
|
subaura?: boolean
|
||||||
extra?: boolean
|
extra?: boolean
|
||||||
|
// Character-specific filters
|
||||||
|
season?: number[]
|
||||||
|
characterSeries?: number[]
|
||||||
|
gachaAvailable?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -44,6 +48,9 @@ export interface SearchPageResult {
|
||||||
totalPages: number
|
totalPages: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Default number of results per page for search queries */
|
||||||
|
const SEARCH_PER_PAGE = 50
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds search parameters from query string and filters
|
* Builds search parameters from query string and filters
|
||||||
*/
|
*/
|
||||||
|
|
@ -51,11 +58,13 @@ function buildSearchParams(
|
||||||
query: string,
|
query: string,
|
||||||
filters: SearchFilters | undefined,
|
filters: SearchFilters | undefined,
|
||||||
page: number,
|
page: number,
|
||||||
locale: 'en' | 'ja' = 'en'
|
locale: 'en' | 'ja' = 'en',
|
||||||
|
exclude?: string[]
|
||||||
): SearchParams {
|
): SearchParams {
|
||||||
const params: SearchParams = {
|
const params: SearchParams = {
|
||||||
page,
|
page,
|
||||||
locale
|
locale,
|
||||||
|
per: SEARCH_PER_PAGE
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only include query if not empty
|
// Only include query if not empty
|
||||||
|
|
@ -63,6 +72,11 @@ function buildSearchParams(
|
||||||
params.query = query.trim()
|
params.query = query.trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only include exclude if provided
|
||||||
|
if (exclude && exclude.length > 0) {
|
||||||
|
params.exclude = exclude
|
||||||
|
}
|
||||||
|
|
||||||
// Build filters object with only defined values
|
// Build filters object with only defined values
|
||||||
if (filters) {
|
if (filters) {
|
||||||
const apiFilters: NonNullable<SearchParams['filters']> = {}
|
const apiFilters: NonNullable<SearchParams['filters']> = {}
|
||||||
|
|
@ -85,6 +99,16 @@ function buildSearchParams(
|
||||||
if (filters.extra !== undefined) {
|
if (filters.extra !== undefined) {
|
||||||
apiFilters.extra = filters.extra
|
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
|
// Only include filters if any were set
|
||||||
if (Object.keys(apiFilters).length > 0) {
|
if (Object.keys(apiFilters).length > 0) {
|
||||||
|
|
@ -154,13 +178,21 @@ export const searchQueries = {
|
||||||
* @param query - Search query string
|
* @param query - Search query string
|
||||||
* @param filters - Optional filter configuration
|
* @param filters - Optional filter configuration
|
||||||
* @param locale - Locale for results (default: 'en')
|
* @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
|
* @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({
|
infiniteQueryOptions({
|
||||||
queryKey: ['search', 'characters', query, filters, locale] as const,
|
queryKey: ['search', 'characters', query, filters, locale, exclude] as const,
|
||||||
queryFn: async ({ pageParam }): Promise<SearchPageResult> => {
|
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)
|
const response = await searchAdapter.searchCharacters(params)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -178,6 +210,7 @@ export const searchQueries = {
|
||||||
},
|
},
|
||||||
staleTime: 1000 * 60 * 5, // 5 minutes
|
staleTime: 1000 * 60 * 5, // 5 minutes
|
||||||
gcTime: 1000 * 60 * 30, // 30 minutes
|
gcTime: 1000 * 60 * 30, // 30 minutes
|
||||||
|
enabled
|
||||||
}),
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue