fix: type errors in svelte-main branch (372 -> 217 errors)

- Fix Button variant errors (outlined -> ghost, contained -> primary)
- Fix search.queries.ts import path and property names (snake_case -> camelCase)
- Fix PartyContext export from party.service.ts
- Fix User type missing avatar property
- Fix exactOptionalPropertyTypes violations in Unit components
- Fix MenuItems Props interface
- Fix RequestOptions, SearchParams, SearchFilters types
- Fix UpdateUncapParams type
- Fix Select.ItemIndicator and maxLength errors
- Fix Summon/Weapon hp/atk properties in entity.adapter.ts

Co-Authored-By: Justin Edmund <justin@jedmund.com>
This commit is contained in:
Devin AI 2025-11-28 20:32:38 +00:00
parent 0afa6c5308
commit cab0a84588
16 changed files with 193 additions and 111 deletions

View file

@ -37,6 +37,29 @@ export interface Weapon {
ulbAttack?: number
transcendenceHp?: number
transcendenceAttack?: number
hp?: {
minHp?: number
maxHp?: number
maxHpFlb?: number
maxHpUlb?: number
}
atk?: {
minAtk?: number
maxAtk?: number
maxAtkFlb?: number
maxAtkUlb?: number
}
uncap?: {
flb?: boolean
ulb?: boolean
transcendence?: boolean
}
maxLevel?: number
skillLevelCap?: number
weapon_skills?: Array<{
name?: string
description?: string
}>
awakenings?: Array<{
id: string
name: Record<string, string>
@ -109,8 +132,33 @@ export interface Summon {
ulbAttack?: number
transcendenceHp?: number
transcendenceAttack?: number
hp?: {
minHp?: number
maxHp?: number
maxHpFlb?: number
maxHpUlb?: number
maxHpXlb?: number
}
atk?: {
minAtk?: number
maxAtk?: number
maxAtkFlb?: number
maxAtkUlb?: number
maxAtkXlb?: number
}
uncap?: {
flb?: boolean
ulb?: boolean
transcendence?: boolean
}
subaura?: boolean
cooldown?: number
callName?: string
callDescription?: string
auraName?: string
auraDescription?: string
subAuraName?: string
subAuraDescription?: string
}
/**

View file

@ -49,11 +49,11 @@ export interface CreateGridSummonParams {
* Parameters for updating uncap levels
*/
export interface UpdateUncapParams {
id?: string
id?: string | undefined
partyId: string
position?: number
position?: number | undefined
uncapLevel: number
transcendenceStep?: number
transcendenceStep?: number | undefined
}
/**

View file

@ -34,28 +34,31 @@ export interface AdapterOptions {
*/
export interface RequestOptions extends Omit<RequestInit, 'body' | 'cache'> {
/** Query parameters to append to the URL */
params?: Record<string, any>
params?: Record<string, any> | undefined
/** Alternative alias for query parameters */
query?: Record<string, any>
query?: Record<string, any> | undefined
/** Request timeout in milliseconds. Overrides the adapter's default timeout */
timeout?: number
timeout?: number | undefined
/** Number of retry attempts for this specific request */
retries?: number
retries?: number | undefined
/** Cache duration for this request in milliseconds */
cacheTime?: number
cacheTime?: number | undefined
/** Request cache mode */
cache?: RequestCache
cache?: RequestCache | undefined
/** Alternative alias for cache duration */
cacheTTL?: number
cacheTTL?: number | undefined
/** Request body. Can be any serializable value */
body?: any
/** HTTP headers for the request */
headers?: Record<string, string> | undefined
}
/**
@ -153,28 +156,28 @@ export interface ApiResponse<T> {
*/
export interface SearchFilters {
/** Filter by element IDs */
element?: number[]
element?: number[] | undefined
/** Filter by rarity levels */
rarity?: number[]
rarity?: number[] | undefined
/** Filter by primary proficiency (weapons and characters) */
proficiency1?: number[]
proficiency1?: number[] | undefined
/** Filter by secondary proficiency (characters only) */
proficiency2?: number[]
proficiency2?: number[] | undefined
/** Filter by series */
series?: number[]
series?: number[] | undefined
/** Include extra/seasonal variants */
extra?: boolean
extra?: boolean | undefined
/** Filter summons with sub-aura */
subaura?: boolean
subaura?: boolean | undefined
/** Filter special characters */
special?: boolean
special?: boolean | undefined
/** Custom filters for specific use cases */
[key: string]: any
@ -188,25 +191,25 @@ export interface SearchParams {
type: 'weapon' | 'character' | 'summon'
/** Search query string */
query?: string
query?: string | undefined
/** Filters to apply to the search */
filters?: SearchFilters
filters?: SearchFilters | undefined
/** Page number for pagination (1-indexed) */
page?: number
page?: number | undefined
/** Number of items per page */
perPage?: number
perPage?: number | undefined
/** Locale for localized content */
locale?: 'en' | 'ja'
locale?: 'en' | 'ja' | undefined
/** Items to exclude from results (by ID) */
exclude?: string[]
exclude?: string[] | undefined
/** AbortSignal for request cancellation */
signal?: AbortSignal
signal?: AbortSignal | undefined
}
/**

View file

@ -9,11 +9,9 @@
import { infiniteQueryOptions } from '@tanstack/svelte-query'
import {
searchWeapons,
searchCharacters,
searchSummons,
searchAdapter,
type SearchParams
} from '$lib/api/resources/search'
} from '$lib/api/adapters/search.adapter'
/**
* Filter configuration for search queries
@ -33,14 +31,14 @@ export interface SearchFilters {
export interface SearchPageResult {
results: Array<{
id: string
granblue_id: string
granblueId: string
name: { en?: string; ja?: string }
element?: number
rarity?: number
proficiency?: number
series?: number
image_url?: string
searchable_type: 'Weapon' | 'Character' | 'Summon'
imageUrl?: string
searchableType: 'Weapon' | 'Character' | 'Summon'
}>
page: number
totalPages: number
@ -131,12 +129,12 @@ export const searchQueries = {
queryKey: ['search', 'weapons', query, filters, locale] as const,
queryFn: async ({ pageParam }): Promise<SearchPageResult> => {
const params = buildSearchParams(query, filters, pageParam, locale)
const response = await searchWeapons(params)
const response = await searchAdapter.searchWeapons(params)
return {
results: response.results,
page: response.meta?.page ?? response.page ?? pageParam,
totalPages: response.meta?.total_pages ?? response.total_pages ?? 1
totalPages: response.meta?.totalPages ?? response.totalPages ?? 1
}
},
initialPageParam: 1,
@ -163,12 +161,12 @@ export const searchQueries = {
queryKey: ['search', 'characters', query, filters, locale] as const,
queryFn: async ({ pageParam }): Promise<SearchPageResult> => {
const params = buildSearchParams(query, filters, pageParam, locale)
const response = await searchCharacters(params)
const response = await searchAdapter.searchCharacters(params)
return {
results: response.results,
page: response.meta?.page ?? response.page ?? pageParam,
totalPages: response.meta?.total_pages ?? response.total_pages ?? 1
totalPages: response.meta?.totalPages ?? response.totalPages ?? 1
}
},
initialPageParam: 1,
@ -195,12 +193,12 @@ export const searchQueries = {
queryKey: ['search', 'summons', query, filters, locale] as const,
queryFn: async ({ pageParam }): Promise<SearchPageResult> => {
const params = buildSearchParams(query, filters, pageParam, locale)
const response = await searchSummons(params)
const response = await searchAdapter.searchSummons(params)
return {
results: response.results,
page: response.meta?.page ?? response.page ?? pageParam,
totalPages: response.meta?.total_pages ?? response.total_pages ?? 1
totalPages: response.meta?.totalPages ?? response.totalPages ?? 1
}
},
initialPageParam: 1,

View file

@ -197,11 +197,11 @@
</div>
<div class="form-actions">
<Button variant="outlined" onclick={handleClose} disabled={saving}>Cancel</Button>
<Button type="submit" variant="contained" disabled={saving}>
{saving ? 'Saving...' : 'Save Changes'}
</Button>
</div>
<Button variant="ghost" onclick={handleClose} disabled={saving}>Cancel</Button>
<Button type="submit" variant="primary" disabled={saving}>
{saving ? 'Saving...' : 'Save Changes'}
</Button>
</div>
</form>
{/snippet}

View file

@ -144,7 +144,7 @@
<div class="error-state">
<Icon name="alert-circle" size={32} />
<p>{error}</p>
<Button size="small" on:click={loadJobs}>Retry</Button>
<Button size="small" onclick={loadJobs}>Retry</Button>
</div>
{:else if Object.keys(filteredJobs).length === 0}
<div class="empty-state">
@ -153,8 +153,8 @@
{#if searchQuery || selectedTiers.size > 0}
<Button
size="small"
variant="outlined"
on:click={() => {
variant="ghost"
onclick={() => {
searchQuery = ''
selectedTiers = new Set(['4', '5', 'ex2', 'o1'])
}}

View file

@ -94,7 +94,7 @@
{disabled}
{readonly}
{required}
{maxLength}
maxlength={maxLength}
{...restProps}
/>
@ -112,16 +112,16 @@
</div>
{:else}
<input
bind:value
class={inputClasses}
{type}
{placeholder}
{disabled}
{readonly}
{required}
{maxLength}
{...restProps}
/>
bind:value
class={inputClasses}
{type}
{placeholder}
{disabled}
{readonly}
{required}
maxlength={maxLength}
{...restProps}
/>
{/if}
{#if error}
@ -143,7 +143,7 @@
{disabled}
{readonly}
{required}
{maxLength}
maxlength={maxLength}
{...restProps}
/>
@ -161,16 +161,16 @@
</div>
{:else}
<input
bind:value
class={inputClasses}
{type}
{placeholder}
{disabled}
{readonly}
{required}
{maxLength}
{...restProps}
/>
bind:value
class={inputClasses}
{type}
{placeholder}
{disabled}
{readonly}
{required}
maxlength={maxLength}
{...restProps}
/>
{/if}
<style lang="scss">

View file

@ -105,13 +105,17 @@
<SelectPrimitive.Viewport>
{#each options as option}
<SelectPrimitive.Item value={String(option.value)} disabled={option.disabled} class="item">
{#if option.image}
<img src={option.image} alt={option.label} class="image" />
{/if}
<span class="text">{option.label}</span>
<SelectPrimitive.ItemIndicator class="indicator">
<Icon name="check" size={14} />
</SelectPrimitive.ItemIndicator>
{#snippet children({ selected })}
{#if option.image}
<img src={option.image} alt={option.label} class="image" />
{/if}
<span class="text">{option.label}</span>
{#if selected}
<span class="indicator">
<Icon name="check" size={14} />
</span>
{/if}
{/snippet}
</SelectPrimitive.Item>
{/each}
</SelectPrimitive.Viewport>
@ -135,14 +139,18 @@
<SelectPrimitive.Content class="content">
<SelectPrimitive.Viewport>
{#each options as option}
<SelectPrimitive.Item value={String(option.value)} disabled={option.disabled} class="item">
{#if option.image}
<img src={option.image} alt={option.label} class="image" />
{/if}
<span class="text">{option.label}</span>
<SelectPrimitive.ItemIndicator class="indicator">
<Icon name="check" size={14} />
</SelectPrimitive.ItemIndicator>
<SelectPrimitive.Item value={String(option.value)} disabled={option.disabled} class="item">
{#snippet children({ selected })}
{#if option.image}
<img src={option.image} alt={option.label} class="image" />
{/if}
<span class="text">{option.label}</span>
{#if selected}
<span class="indicator">
<Icon name="check" size={14} />
</span>
{/if}
{/snippet}
</SelectPrimitive.Item>
{/each}
</SelectPrimitive.Viewport>

View file

@ -2,14 +2,14 @@
import { ContextMenu, DropdownMenu } from 'bits-ui'
interface MenuItemsProps {
onViewDetails?: () => void
onReplace?: () => void
onRemove?: () => void
canEdit?: boolean
onViewDetails?: (() => void) | undefined
onReplace?: (() => void) | undefined
onRemove?: (() => void | Promise<void>) | undefined
canEdit?: boolean | undefined
variant?: 'context' | 'dropdown'
viewDetailsLabel?: string
replaceLabel?: string
removeLabel?: string
viewDetailsLabel?: string | undefined
replaceLabel?: string | undefined
removeLabel?: string | undefined
}
let {

View file

@ -16,11 +16,11 @@
import * as m from '$lib/paraglide/messages'
interface Props {
item?: GridCharacter
item?: GridCharacter | undefined
position: number
mainWeaponElement?: number | null
partyElement?: number | null
job?: Job
mainWeaponElement?: number | null | undefined
partyElement?: number | null | undefined
job?: Job | undefined
}
let { item, position, mainWeaponElement, partyElement, job }: Props = $props()

View file

@ -12,7 +12,7 @@
import * as m from '$lib/paraglide/messages'
interface Props {
item?: GridSummon
item?: GridSummon | undefined
position: number
}
@ -24,6 +24,7 @@
canEdit: () => boolean
getEditKey: () => string | null
services: { gridService: any; partyService: any }
openPicker?: (opts: { type: 'weapon' | 'summon' | 'character'; position: number; item?: any }) => void
}
const ctx = getContext<PartyCtx>('party')

View file

@ -13,7 +13,7 @@
import * as m from '$lib/paraglide/messages'
interface Props {
item?: GridWeapon
item?: GridWeapon | undefined
position: number
}
@ -25,6 +25,7 @@
canEdit: () => boolean
getEditKey: () => string | null
services: { gridService: any; partyService: any }
openPicker?: (opts: { type: 'weapon' | 'summon' | 'character'; position: number; item?: any }) => void
}
const ctx = getContext<PartyCtx>('party')

View file

@ -3,6 +3,18 @@ import { partyAdapter } from '$lib/api/adapters/party.adapter'
import { authStore } from '$lib/stores/auth.store'
import { browser } from '$app/environment'
/**
* Context type for party-related operations in components
*/
export interface PartyContext {
getParty: () => Party
updateParty: (p: Party) => void
canEdit: () => boolean
getEditKey: () => string | null
services: { gridService: any; partyService: any }
openPicker?: (opts: { type: 'weapon' | 'summon' | 'character'; position: number; item?: any }) => void
}
export interface EditabilityResult {
canEdit: boolean
headers?: Record<string, string>

View file

@ -194,4 +194,8 @@ export interface User {
role?: string
createdAt?: string
updatedAt?: string
avatar?: {
picture?: string
element?: string
}
}

View file

@ -204,20 +204,20 @@
</div>
<div class="form-actions">
<Button
variant="outlined"
href="/me"
>
Cancel
</Button>
<Button
type="submit"
variant="contained"
disabled={saving}
>
{saving ? 'Saving...' : 'Save Changes'}
</Button>
</div>
<Button
variant="ghost"
href="/me"
>
Cancel
</Button>
<Button
type="submit"
variant="primary"
disabled={saving}
>
{saving ? 'Saving...' : 'Save Changes'}
</Button>
</div>
</form>
</div>
</div>
@ -363,4 +363,4 @@
padding-top: spacing.$unit-2x;
border-top: 1px solid var(--border-color);
}
</style>
</style>

View file

@ -98,6 +98,12 @@
if (!partyId && !isCreatingParty && items.length > 0) {
isCreatingParty = true
const firstItem = items[0]
// Guard against undefined firstItem (shouldn't happen given items.length > 0 check, but TypeScript needs this)
if (!firstItem) {
isCreatingParty = false
return
}
try {
// Step 1: Create the party (with local_id only for anonymous users)
@ -262,6 +268,7 @@
try {
for (let i = 0; i < items.length; i++) {
const item = items[i]
if (!item) continue // Skip undefined items
let position = -1 // Default position
if (activeTab === GridType.Weapon) {