extend DatabaseGridWithProvider to support jobs
- add searchJobs method and job-specific filters to search adapter - add jobs case to DatabaseProvider - extend DatabaseGridWithProvider for jobs resource - hide collection filters for jobs (no element/rarity/series) - extend filterParams and listNavigation for job entity
This commit is contained in:
parent
23527c727e
commit
ec4b4bc3ae
6 changed files with 114 additions and 26 deletions
|
|
@ -377,6 +377,67 @@ export class SearchAdapter extends BaseAdapter {
|
|||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for jobs with specific filters
|
||||
*
|
||||
* @param params - Search parameters with job-specific filters
|
||||
* @returns Promise resolving to job search results
|
||||
*/
|
||||
async searchJobs(params: SearchParams = {}): Promise<SearchResponse> {
|
||||
const body: any = {
|
||||
locale: params.locale || 'en',
|
||||
page: params.page || 1
|
||||
}
|
||||
|
||||
if (params.per) {
|
||||
body.per = params.per
|
||||
}
|
||||
|
||||
if (params.query) {
|
||||
body.query = params.query
|
||||
}
|
||||
|
||||
if (params.sortBy) {
|
||||
body.sort = params.sortBy
|
||||
}
|
||||
if (params.sortOrder) {
|
||||
body.order = params.sortOrder
|
||||
}
|
||||
|
||||
// Build job-specific filters
|
||||
if (params.filters) {
|
||||
const filters: any = {}
|
||||
|
||||
if (params.filters.row?.length) {
|
||||
filters.row = params.filters.row
|
||||
}
|
||||
if (params.filters.proficiency?.length) {
|
||||
filters.proficiency = params.filters.proficiency
|
||||
}
|
||||
if (params.filters.masterLevel !== undefined) {
|
||||
filters.masterLevel = params.filters.masterLevel
|
||||
}
|
||||
if (params.filters.ultimateMastery !== undefined) {
|
||||
filters.ultimateMastery = params.filters.ultimateMastery
|
||||
}
|
||||
if (params.filters.accessory !== undefined) {
|
||||
filters.accessory = params.filters.accessory
|
||||
}
|
||||
|
||||
if (Object.keys(filters).length > 0) {
|
||||
body.filters = filters
|
||||
}
|
||||
}
|
||||
|
||||
return this.request<SearchResponse>('/search/jobs', {
|
||||
method: 'POST',
|
||||
body: { search: body },
|
||||
credentials: 'omit',
|
||||
cacheTTL: params.query ? 300000 : 0,
|
||||
headers: params.per ? { 'X-Per-Page': String(params.per) } : undefined
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all cached search results
|
||||
* Useful when entity data has been updated
|
||||
|
|
|
|||
|
|
@ -191,6 +191,23 @@ export interface SearchFilters {
|
|||
/** Filter special characters */
|
||||
special?: boolean | undefined
|
||||
|
||||
// Job-specific filters
|
||||
|
||||
/** Filter by job row (1-5, ex, ex2, o1) */
|
||||
row?: string[] | undefined
|
||||
|
||||
/** Filter by proficiency (for jobs - matches either proficiency1 or proficiency2) */
|
||||
proficiency?: number[] | undefined
|
||||
|
||||
/** Filter jobs with master level */
|
||||
masterLevel?: boolean | undefined
|
||||
|
||||
/** Filter jobs with ultimate mastery */
|
||||
ultimateMastery?: boolean | undefined
|
||||
|
||||
/** Filter jobs that support accessories */
|
||||
accessory?: boolean | undefined
|
||||
|
||||
/** Custom filters for specific use cases */
|
||||
[key: string]: any
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
import type { Snippet } from 'svelte'
|
||||
|
||||
interface Props {
|
||||
resource: 'weapons' | 'characters' | 'summons'
|
||||
resource: 'weapons' | 'characters' | 'summons' | 'jobs'
|
||||
columns: IColumn[]
|
||||
pageSize?: number
|
||||
leftActions?: Snippet
|
||||
|
|
@ -45,9 +45,14 @@
|
|||
|
||||
// Derive entity type from resource
|
||||
const entityType = $derived(
|
||||
resource === 'characters' ? 'character' : resource === 'summons' ? 'summon' : 'weapon'
|
||||
resource === 'characters' ? 'character' :
|
||||
resource === 'summons' ? 'summon' :
|
||||
resource === 'jobs' ? 'job' : 'weapon'
|
||||
)
|
||||
|
||||
// Jobs don't use the standard CollectionFilters component
|
||||
const supportsCollectionFilters = $derived(resource !== 'jobs')
|
||||
|
||||
// Fetch weapon series list for URL slug mapping (only for weapons)
|
||||
const weaponSeriesQuery = createQuery(() =>
|
||||
queryOptions({
|
||||
|
|
@ -403,29 +408,31 @@
|
|||
{@render headerActions()}
|
||||
{/if}
|
||||
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="small"
|
||||
onclick={() => (showFilters = !showFilters)}
|
||||
class="filter-toggle {hasActiveFilters ? 'has-active' : ''}"
|
||||
>
|
||||
Filters
|
||||
{#if hasActiveFilters}
|
||||
<span class="filter-count {selectedElement ?? ''}">
|
||||
{elementFilters.length +
|
||||
rarityFilters.length +
|
||||
seriesFilters.length +
|
||||
proficiencyFilters.length +
|
||||
seasonFilters.length}
|
||||
</span>
|
||||
{/if}
|
||||
</Button>
|
||||
{#if supportsCollectionFilters}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="small"
|
||||
onclick={() => (showFilters = !showFilters)}
|
||||
class="filter-toggle {hasActiveFilters ? 'has-active' : ''}"
|
||||
>
|
||||
Filters
|
||||
{#if hasActiveFilters}
|
||||
<span class="filter-count {selectedElement ?? ''}">
|
||||
{elementFilters.length +
|
||||
rarityFilters.length +
|
||||
seriesFilters.length +
|
||||
proficiencyFilters.length +
|
||||
seasonFilters.length}
|
||||
</span>
|
||||
{/if}
|
||||
</Button>
|
||||
{/if}
|
||||
|
||||
<input type="text" placeholder="Search..." bind:value={searchTerm} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if showFilters}
|
||||
{#if showFilters && supportsCollectionFilters}
|
||||
<div class="filters-row">
|
||||
<CollectionFilters
|
||||
entityType={resource === 'characters'
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import type { SearchParams } from '$lib/api/adapters/search.adapter'
|
|||
import type { SearchFilters } from '$lib/api/adapters/types'
|
||||
|
||||
interface DatabaseProviderOptions {
|
||||
resource: 'weapons' | 'characters' | 'summons'
|
||||
resource: 'weapons' | 'characters' | 'summons' | 'jobs'
|
||||
pageSize?: number
|
||||
}
|
||||
|
||||
|
|
@ -19,7 +19,7 @@ interface APIResponse {
|
|||
}
|
||||
|
||||
export class DatabaseProvider extends RestDataProvider<any> {
|
||||
private resource: 'weapons' | 'characters' | 'summons'
|
||||
private resource: 'weapons' | 'characters' | 'summons' | 'jobs'
|
||||
private pageSize: number
|
||||
private currentPage: number = 1
|
||||
private totalCount: number = 0
|
||||
|
|
@ -72,6 +72,9 @@ export class DatabaseProvider extends RestDataProvider<any> {
|
|||
case 'summons':
|
||||
result = await searchAdapter.searchSummons(searchParams)
|
||||
break
|
||||
case 'jobs':
|
||||
result = await searchAdapter.searchJobs(searchParams)
|
||||
break
|
||||
default:
|
||||
throw new Error(`Unknown resource type: ${this.resource}`)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ function parseParamArray<T>(
|
|||
*/
|
||||
export function parseFiltersFromUrl(
|
||||
searchParams: URLSearchParams,
|
||||
entityType: 'character' | 'weapon' | 'summon',
|
||||
entityType: 'character' | 'weapon' | 'summon' | 'job',
|
||||
weaponSeriesList?: WeaponSeries[]
|
||||
): ParsedFilters {
|
||||
const element = parseParamArray(searchParams, 'element', PARAM_TO_ELEMENT)
|
||||
|
|
@ -252,7 +252,7 @@ export function buildUrlFromFilters(
|
|||
filters: CollectionFilterState,
|
||||
searchQuery: string,
|
||||
page: number,
|
||||
entityType: 'character' | 'weapon' | 'summon',
|
||||
entityType: 'character' | 'weapon' | 'summon' | 'job',
|
||||
weaponSeriesList?: WeaponSeries[]
|
||||
): URLSearchParams {
|
||||
const params = new URLSearchParams()
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ const STORAGE_KEY = 'database_list_url'
|
|||
|
||||
interface StoredListUrl {
|
||||
url: string
|
||||
resource: 'characters' | 'weapons' | 'summons'
|
||||
resource: 'characters' | 'weapons' | 'summons' | 'jobs'
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the current list URL before navigating to a detail page
|
||||
*/
|
||||
export function storeListUrl(url: string, resource: 'characters' | 'weapons' | 'summons'): void {
|
||||
export function storeListUrl(url: string, resource: 'characters' | 'weapons' | 'summons' | 'jobs'): void {
|
||||
try {
|
||||
const data: StoredListUrl = { url, resource }
|
||||
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(data))
|
||||
|
|
|
|||
Loading…
Reference in a new issue