add job accessory types and adapter methods
- add accessoryType field to JobAccessory entity - add accessory CRUD methods to job adapter - add accessory query options - add jobAccessoryUtils with type helpers
This commit is contained in:
parent
4401191738
commit
29211709ef
6 changed files with 232 additions and 26 deletions
|
|
@ -42,6 +42,19 @@ export interface JobSkillPayload {
|
||||||
action_id?: number
|
action_id?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Payload for creating/updating a job accessory
|
||||||
|
*/
|
||||||
|
export interface JobAccessoryPayload {
|
||||||
|
name_en: string
|
||||||
|
name_jp?: string
|
||||||
|
granblue_id: string
|
||||||
|
rarity?: number
|
||||||
|
release_date?: string
|
||||||
|
accessory_type: number // 1 = Shield, 2 = Manatura
|
||||||
|
job_id?: string
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Payload for updating a job entity
|
* Payload for updating a job entity
|
||||||
*/
|
*/
|
||||||
|
|
@ -123,15 +136,25 @@ export class JobAdapter extends BaseAdapter {
|
||||||
* Gets all accessories available for a specific job
|
* Gets all accessories available for a specific job
|
||||||
* Only returns data if the job supports accessories
|
* Only returns data if the job supports accessories
|
||||||
*/
|
*/
|
||||||
async getAccessories(jobId: string): Promise<JobAccessory[]> {
|
async getAccessoriesForJob(jobId: string): Promise<JobAccessory[]> {
|
||||||
const response = await this.request<{ accessories: JobAccessory[] }>(
|
return this.request<JobAccessory[]>(`/jobs/${jobId}/accessories`, {
|
||||||
`/jobs/${jobId}/accessories`,
|
method: 'GET',
|
||||||
{
|
cacheTTL: 300000 // Cache for 5 minutes
|
||||||
method: 'GET',
|
})
|
||||||
cacheTTL: 300000 // Cache for 5 minutes
|
}
|
||||||
}
|
|
||||||
)
|
/**
|
||||||
return response.accessories
|
* Creates a new job entity (database admin function)
|
||||||
|
* @param data The job data
|
||||||
|
*/
|
||||||
|
async createJob(data: JobUpdatePayload): Promise<Job> {
|
||||||
|
const response = await this.request<Job>('/jobs', {
|
||||||
|
method: 'POST',
|
||||||
|
body: data
|
||||||
|
})
|
||||||
|
// Clear jobs cache to reflect the new job
|
||||||
|
this.clearCache('/jobs')
|
||||||
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -327,12 +350,78 @@ export class JobAdapter extends BaseAdapter {
|
||||||
this.clearCache(`/parties/${partyId}`)
|
this.clearCache(`/parties/${partyId}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// Job Accessory Methods (Database Management)
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all job accessories
|
||||||
|
* @param accessoryType Optional filter by type (1=Shield, 2=Manatura)
|
||||||
|
*/
|
||||||
|
async getAllAccessories(accessoryType?: number): Promise<JobAccessory[]> {
|
||||||
|
const params = accessoryType ? `?accessory_type=${accessoryType}` : ''
|
||||||
|
return this.request<JobAccessory[]>(`/job_accessories${params}`, {
|
||||||
|
method: 'GET',
|
||||||
|
cacheTTL: 300000 // Cache for 5 minutes
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a single job accessory by ID or granblue_id
|
||||||
|
*/
|
||||||
|
async getAccessoryById(id: string): Promise<JobAccessory> {
|
||||||
|
return this.request<JobAccessory>(`/job_accessories/${id}`, {
|
||||||
|
method: 'GET',
|
||||||
|
cacheTTL: 300000 // Cache for 5 minutes
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new job accessory
|
||||||
|
* @param data The accessory data
|
||||||
|
*/
|
||||||
|
async createAccessory(data: JobAccessoryPayload): Promise<JobAccessory> {
|
||||||
|
const response = await this.request<JobAccessory>('/job_accessories', {
|
||||||
|
method: 'POST',
|
||||||
|
body: data
|
||||||
|
})
|
||||||
|
this.clearCache('/job_accessories')
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates an existing job accessory
|
||||||
|
* @param id The accessory's ID or granblue_id
|
||||||
|
* @param data The updated accessory data
|
||||||
|
*/
|
||||||
|
async updateAccessory(id: string, data: Partial<JobAccessoryPayload>): Promise<JobAccessory> {
|
||||||
|
const response = await this.request<JobAccessory>(`/job_accessories/${id}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
body: data
|
||||||
|
})
|
||||||
|
this.clearCache('/job_accessories')
|
||||||
|
this.clearCache(`/job_accessories/${id}`)
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a job accessory
|
||||||
|
* @param id The accessory's ID or granblue_id
|
||||||
|
*/
|
||||||
|
async deleteAccessory(id: string): Promise<void> {
|
||||||
|
await this.request(`/job_accessories/${id}`, {
|
||||||
|
method: 'DELETE'
|
||||||
|
})
|
||||||
|
this.clearCache('/job_accessories')
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears the cache for job-related data
|
* Clears the cache for job-related data
|
||||||
*/
|
*/
|
||||||
clearJobCache() {
|
clearJobCache() {
|
||||||
this.clearCache('/jobs')
|
this.clearCache('/jobs')
|
||||||
this.clearCache('/search/job_skills')
|
this.clearCache('/search/job_skills')
|
||||||
|
this.clearCache('/job_accessories')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -140,15 +140,15 @@ export const jobQueries = {
|
||||||
}),
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Job accessories query options
|
* Job accessories query options (for a specific job)
|
||||||
*
|
*
|
||||||
* @param jobId - Job ID to fetch accessories for
|
* @param jobId - Job ID to fetch accessories for
|
||||||
* @returns Query options for fetching job accessories
|
* @returns Query options for fetching job accessories
|
||||||
*/
|
*/
|
||||||
accessories: (jobId: string) =>
|
accessoriesForJob: (jobId: string) =>
|
||||||
queryOptions({
|
queryOptions({
|
||||||
queryKey: ['jobs', jobId, 'accessories'] as const,
|
queryKey: ['jobs', jobId, 'accessories'] as const,
|
||||||
queryFn: () => jobAdapter.getAccessories(jobId),
|
queryFn: () => jobAdapter.getAccessoriesForJob(jobId),
|
||||||
enabled: !!jobId,
|
enabled: !!jobId,
|
||||||
staleTime: 1000 * 60 * 30, // 30 minutes
|
staleTime: 1000 * 60 * 30, // 30 minutes
|
||||||
gcTime: 1000 * 60 * 60 // 1 hour
|
gcTime: 1000 * 60 * 60 // 1 hour
|
||||||
|
|
@ -165,6 +165,39 @@ export const jobQueries = {
|
||||||
queryFn: () => jobAdapter.getAllSkills(),
|
queryFn: () => jobAdapter.getAllSkills(),
|
||||||
staleTime: 1000 * 60 * 30, // 30 minutes
|
staleTime: 1000 * 60 * 30, // 30 minutes
|
||||||
gcTime: 1000 * 60 * 60 // 1 hour
|
gcTime: 1000 * 60 * 60 // 1 hour
|
||||||
|
}),
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// Job Accessory Database Queries
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All job accessories list query options
|
||||||
|
*
|
||||||
|
* @param accessoryType - Optional filter by type (1=Shield, 2=Manatura)
|
||||||
|
* @returns Query options for fetching all job accessories
|
||||||
|
*/
|
||||||
|
accessoriesList: (accessoryType?: number) =>
|
||||||
|
queryOptions({
|
||||||
|
queryKey: ['jobAccessories', { accessoryType }] as const,
|
||||||
|
queryFn: () => jobAdapter.getAllAccessories(accessoryType),
|
||||||
|
staleTime: 1000 * 60 * 30, // 30 minutes
|
||||||
|
gcTime: 1000 * 60 * 60 // 1 hour
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Single job accessory query options
|
||||||
|
*
|
||||||
|
* @param id - Accessory ID or granblue_id
|
||||||
|
* @returns Query options for fetching a single accessory
|
||||||
|
*/
|
||||||
|
accessoryById: (id: string) =>
|
||||||
|
queryOptions({
|
||||||
|
queryKey: ['jobAccessories', id] as const,
|
||||||
|
queryFn: () => jobAdapter.getAccessoryById(id),
|
||||||
|
enabled: !!id,
|
||||||
|
staleTime: 1000 * 60 * 30, // 30 minutes
|
||||||
|
gcTime: 1000 * 60 * 60 // 1 hour
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -193,6 +226,15 @@ export const jobKeys = {
|
||||||
empSkills: (jobId: string) => [...jobKeys.all, jobId, 'emp_skills'] as const,
|
empSkills: (jobId: string) => [...jobKeys.all, jobId, 'emp_skills'] as const,
|
||||||
skillsSearch: (jobId: string, params?: Omit<SearchJobSkillsParams, 'jobId' | 'page'>) =>
|
skillsSearch: (jobId: string, params?: Omit<SearchJobSkillsParams, 'jobId' | 'page'>) =>
|
||||||
[...jobKeys.skills(jobId), 'search', params] as const,
|
[...jobKeys.skills(jobId), 'search', params] as const,
|
||||||
accessories: (jobId: string) => [...jobKeys.all, jobId, 'accessories'] as const,
|
accessoriesForJob: (jobId: string) => [...jobKeys.all, jobId, 'accessories'] as const,
|
||||||
allSkills: () => [...jobKeys.all, 'skills', 'all'] as const
|
allSkills: () => [...jobKeys.all, 'skills', 'all'] as const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Job accessory query key helpers for cache invalidation
|
||||||
|
*/
|
||||||
|
export const jobAccessoryKeys = {
|
||||||
|
all: ['jobAccessories'] as const,
|
||||||
|
lists: (accessoryType?: number) => [...jobAccessoryKeys.all, { accessoryType }] as const,
|
||||||
|
detail: (id: string) => [...jobAccessoryKeys.all, id] as const
|
||||||
|
}
|
||||||
|
|
|
||||||
13
src/lib/types/JobAccessory.d.ts
vendored
13
src/lib/types/JobAccessory.d.ts
vendored
|
|
@ -1,11 +1,2 @@
|
||||||
export interface JobAccessory {
|
// Re-export from entities for backwards compatibility
|
||||||
id: string
|
export type { JobAccessory } from './api/entities'
|
||||||
granblue_id: string
|
|
||||||
job: Job
|
|
||||||
name: {
|
|
||||||
[key: string]: string
|
|
||||||
en: string
|
|
||||||
ja: string
|
|
||||||
}
|
|
||||||
rarity: number
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -201,12 +201,15 @@ export interface JobSkill {
|
||||||
actionId?: number // Unique game ID
|
actionId?: number // Unique game ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// JobAccessory entity
|
// JobAccessory entity from JobAccessoryBlueprint
|
||||||
export interface JobAccessory {
|
export interface JobAccessory {
|
||||||
id: string
|
id: string
|
||||||
name: LocalizedName
|
name: LocalizedName
|
||||||
slug: string
|
|
||||||
granblueId: string
|
granblueId: string
|
||||||
|
rarity: number
|
||||||
|
releaseDate?: string
|
||||||
|
accessoryType: number // 1 = Shield, 2 = Manatura
|
||||||
|
job?: Job // Associated job (optional, included when available)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Raid entity from RaidBlueprint
|
// Raid entity from RaidBlueprint
|
||||||
|
|
|
||||||
50
src/lib/utils/jobAccessoryUtils.ts
Normal file
50
src/lib/utils/jobAccessoryUtils.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
/**
|
||||||
|
* Job Accessory Utilities
|
||||||
|
*
|
||||||
|
* Helper functions for working with job accessories (Shields and Manatura).
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accessory type constants
|
||||||
|
*/
|
||||||
|
export const ACCESSORY_TYPES = {
|
||||||
|
SHIELD: 1,
|
||||||
|
MANATURA: 2
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type AccessoryType = (typeof ACCESSORY_TYPES)[keyof typeof ACCESSORY_TYPES]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the display name for an accessory type
|
||||||
|
*/
|
||||||
|
export function getAccessoryTypeName(type: number): string {
|
||||||
|
switch (type) {
|
||||||
|
case ACCESSORY_TYPES.SHIELD:
|
||||||
|
return 'Shield'
|
||||||
|
case ACCESSORY_TYPES.MANATURA:
|
||||||
|
return 'Manatura'
|
||||||
|
default:
|
||||||
|
return 'Unknown'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets options for accessory type filter/select
|
||||||
|
*/
|
||||||
|
export function getAccessoryTypeOptions(): Array<{ value: number; label: string }> {
|
||||||
|
return [
|
||||||
|
{ value: ACCESSORY_TYPES.SHIELD, label: 'Shield' },
|
||||||
|
{ value: ACCESSORY_TYPES.MANATURA, label: 'Manatura' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the image URL for a job accessory
|
||||||
|
* @param granblueId The accessory's granblue_id
|
||||||
|
* @param accessoryType The type of accessory (1=Shield, 2=Manatura)
|
||||||
|
*/
|
||||||
|
export function getJobAccessoryImageUrl(granblueId: string, accessoryType: number): string {
|
||||||
|
// Different asset paths based on accessory type
|
||||||
|
const folder = accessoryType === ACCESSORY_TYPES.SHIELD ? 'shield' : 'manatura'
|
||||||
|
return `/images/job-accessories/${folder}/${granblueId}.png`
|
||||||
|
}
|
||||||
|
|
@ -290,3 +290,34 @@ export function validateSkillConfiguration(
|
||||||
errors
|
errors
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proficiency options for job forms
|
||||||
|
*/
|
||||||
|
export const PROFICIENCIES = [
|
||||||
|
{ value: 0, label: 'None' },
|
||||||
|
{ value: 1, label: 'Sabre' },
|
||||||
|
{ value: 2, label: 'Dagger' },
|
||||||
|
{ value: 3, label: 'Axe' },
|
||||||
|
{ value: 4, label: 'Spear' },
|
||||||
|
{ value: 5, label: 'Bow' },
|
||||||
|
{ value: 6, label: 'Staff' },
|
||||||
|
{ value: 7, label: 'Melee' },
|
||||||
|
{ value: 8, label: 'Harp' },
|
||||||
|
{ value: 9, label: 'Gun' },
|
||||||
|
{ value: 10, label: 'Katana' }
|
||||||
|
] as const
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Row options for job forms
|
||||||
|
*/
|
||||||
|
export const ROWS = [
|
||||||
|
{ value: '1', label: 'Class I' },
|
||||||
|
{ value: '2', label: 'Class II' },
|
||||||
|
{ value: '3', label: 'Class III' },
|
||||||
|
{ value: '4', label: 'Class IV' },
|
||||||
|
{ value: '5', label: 'Class V' },
|
||||||
|
{ value: 'ex', label: 'EX' },
|
||||||
|
{ value: 'ex2', label: 'EX II' },
|
||||||
|
{ value: 'o1', label: 'Origin I' }
|
||||||
|
] as const
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue