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
|
* Clears all cached search results
|
||||||
* Useful when entity data has been updated
|
* Useful when entity data has been updated
|
||||||
|
|
|
||||||
|
|
@ -191,6 +191,23 @@ export interface SearchFilters {
|
||||||
/** Filter special characters */
|
/** Filter special characters */
|
||||||
special?: boolean | undefined
|
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 */
|
/** Custom filters for specific use cases */
|
||||||
[key: string]: any
|
[key: string]: any
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
import type { Snippet } from 'svelte'
|
import type { Snippet } from 'svelte'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
resource: 'weapons' | 'characters' | 'summons'
|
resource: 'weapons' | 'characters' | 'summons' | 'jobs'
|
||||||
columns: IColumn[]
|
columns: IColumn[]
|
||||||
pageSize?: number
|
pageSize?: number
|
||||||
leftActions?: Snippet
|
leftActions?: Snippet
|
||||||
|
|
@ -45,9 +45,14 @@
|
||||||
|
|
||||||
// Derive entity type from resource
|
// Derive entity type from resource
|
||||||
const entityType = $derived(
|
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)
|
// Fetch weapon series list for URL slug mapping (only for weapons)
|
||||||
const weaponSeriesQuery = createQuery(() =>
|
const weaponSeriesQuery = createQuery(() =>
|
||||||
queryOptions({
|
queryOptions({
|
||||||
|
|
@ -403,29 +408,31 @@
|
||||||
{@render headerActions()}
|
{@render headerActions()}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<Button
|
{#if supportsCollectionFilters}
|
||||||
variant="ghost"
|
<Button
|
||||||
size="small"
|
variant="ghost"
|
||||||
onclick={() => (showFilters = !showFilters)}
|
size="small"
|
||||||
class="filter-toggle {hasActiveFilters ? 'has-active' : ''}"
|
onclick={() => (showFilters = !showFilters)}
|
||||||
>
|
class="filter-toggle {hasActiveFilters ? 'has-active' : ''}"
|
||||||
Filters
|
>
|
||||||
{#if hasActiveFilters}
|
Filters
|
||||||
<span class="filter-count {selectedElement ?? ''}">
|
{#if hasActiveFilters}
|
||||||
{elementFilters.length +
|
<span class="filter-count {selectedElement ?? ''}">
|
||||||
rarityFilters.length +
|
{elementFilters.length +
|
||||||
seriesFilters.length +
|
rarityFilters.length +
|
||||||
proficiencyFilters.length +
|
seriesFilters.length +
|
||||||
seasonFilters.length}
|
proficiencyFilters.length +
|
||||||
</span>
|
seasonFilters.length}
|
||||||
{/if}
|
</span>
|
||||||
</Button>
|
{/if}
|
||||||
|
</Button>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<input type="text" placeholder="Search..." bind:value={searchTerm} />
|
<input type="text" placeholder="Search..." bind:value={searchTerm} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if showFilters}
|
{#if showFilters && supportsCollectionFilters}
|
||||||
<div class="filters-row">
|
<div class="filters-row">
|
||||||
<CollectionFilters
|
<CollectionFilters
|
||||||
entityType={resource === 'characters'
|
entityType={resource === 'characters'
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import type { SearchParams } from '$lib/api/adapters/search.adapter'
|
||||||
import type { SearchFilters } from '$lib/api/adapters/types'
|
import type { SearchFilters } from '$lib/api/adapters/types'
|
||||||
|
|
||||||
interface DatabaseProviderOptions {
|
interface DatabaseProviderOptions {
|
||||||
resource: 'weapons' | 'characters' | 'summons'
|
resource: 'weapons' | 'characters' | 'summons' | 'jobs'
|
||||||
pageSize?: number
|
pageSize?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -19,7 +19,7 @@ interface APIResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DatabaseProvider extends RestDataProvider<any> {
|
export class DatabaseProvider extends RestDataProvider<any> {
|
||||||
private resource: 'weapons' | 'characters' | 'summons'
|
private resource: 'weapons' | 'characters' | 'summons' | 'jobs'
|
||||||
private pageSize: number
|
private pageSize: number
|
||||||
private currentPage: number = 1
|
private currentPage: number = 1
|
||||||
private totalCount: number = 0
|
private totalCount: number = 0
|
||||||
|
|
@ -72,6 +72,9 @@ export class DatabaseProvider extends RestDataProvider<any> {
|
||||||
case 'summons':
|
case 'summons':
|
||||||
result = await searchAdapter.searchSummons(searchParams)
|
result = await searchAdapter.searchSummons(searchParams)
|
||||||
break
|
break
|
||||||
|
case 'jobs':
|
||||||
|
result = await searchAdapter.searchJobs(searchParams)
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown resource type: ${this.resource}`)
|
throw new Error(`Unknown resource type: ${this.resource}`)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,7 @@ function parseParamArray<T>(
|
||||||
*/
|
*/
|
||||||
export function parseFiltersFromUrl(
|
export function parseFiltersFromUrl(
|
||||||
searchParams: URLSearchParams,
|
searchParams: URLSearchParams,
|
||||||
entityType: 'character' | 'weapon' | 'summon',
|
entityType: 'character' | 'weapon' | 'summon' | 'job',
|
||||||
weaponSeriesList?: WeaponSeries[]
|
weaponSeriesList?: WeaponSeries[]
|
||||||
): ParsedFilters {
|
): ParsedFilters {
|
||||||
const element = parseParamArray(searchParams, 'element', PARAM_TO_ELEMENT)
|
const element = parseParamArray(searchParams, 'element', PARAM_TO_ELEMENT)
|
||||||
|
|
@ -252,7 +252,7 @@ export function buildUrlFromFilters(
|
||||||
filters: CollectionFilterState,
|
filters: CollectionFilterState,
|
||||||
searchQuery: string,
|
searchQuery: string,
|
||||||
page: number,
|
page: number,
|
||||||
entityType: 'character' | 'weapon' | 'summon',
|
entityType: 'character' | 'weapon' | 'summon' | 'job',
|
||||||
weaponSeriesList?: WeaponSeries[]
|
weaponSeriesList?: WeaponSeries[]
|
||||||
): URLSearchParams {
|
): URLSearchParams {
|
||||||
const params = new URLSearchParams()
|
const params = new URLSearchParams()
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,13 @@ const STORAGE_KEY = 'database_list_url'
|
||||||
|
|
||||||
interface StoredListUrl {
|
interface StoredListUrl {
|
||||||
url: string
|
url: string
|
||||||
resource: 'characters' | 'weapons' | 'summons'
|
resource: 'characters' | 'weapons' | 'summons' | 'jobs'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store the current list URL before navigating to a detail page
|
* 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 {
|
try {
|
||||||
const data: StoredListUrl = { url, resource }
|
const data: StoredListUrl = { url, resource }
|
||||||
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(data))
|
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(data))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue