From 5ddcecaece052f4b713727b8589dc3da5c239dfe Mon Sep 17 00:00:00 2001 From: Erik Date: Wed, 15 Jul 2026 06:09:22 +0200 Subject: [PATCH] feat(frontend): inventory search types + filter/column constants Co-Authored-By: Claude Sonnet 5 --- .../src/components/inventory/constants.ts | 101 ++++++++++++++++++ frontend/src/components/inventory/types.ts | 67 ++++++++++++ 2 files changed, 168 insertions(+) create mode 100644 frontend/src/components/inventory/constants.ts create mode 100644 frontend/src/components/inventory/types.ts diff --git a/frontend/src/components/inventory/constants.ts b/frontend/src/components/inventory/constants.ts new file mode 100644 index 00000000..fb1e8610 --- /dev/null +++ b/frontend/src/components/inventory/constants.ts @@ -0,0 +1,101 @@ +export interface CantripDef { value: string; label: string; } +export interface CantripGroup { group: string; items: CantripDef[]; } + +const c = (value: string): CantripDef => ({ value, label: value.replace(/^Legendary /, '') }); + +export const CANTRIP_GROUPS: CantripGroup[] = [ + { group: 'Attributes', items: [ + c('Legendary Strength'), c('Legendary Endurance'), c('Legendary Quickness'), + c('Legendary Coordination'), c('Legendary Willpower'), c('Legendary Focus'), + ]}, + { group: 'Weapon skills', items: [ + c('Legendary Heavy Weapon Aptitude'), c('Legendary Light Weapon Aptitude'), + c('Legendary Finesse Weapon Aptitude'), c('Legendary Missile Weapon Aptitude'), + c('Legendary Two Handed Combat Aptitude'), c('Legendary Dual Wield Aptitude'), + c('Legendary Shield Aptitude'), c('Legendary Sneak Attack Prowess'), + c('Legendary Dirty Fighting Prowess'), c('Legendary Recklessness Prowess'), + c('Legendary Defender'), c('Legendary Blood Thirst'), + ]}, + { group: 'Magic', items: [ + c('Legendary War Magic Aptitude'), c('Legendary Void Magic Aptitude'), + c('Legendary Creature Enchantment Aptitude'), c('Legendary Item Enchantment Aptitude'), + c('Legendary Life Magic Aptitude'), c('Legendary Mana Conversion Prowess'), + c('Legendary Arcane Prowess'), c('Legendary Hermetic Link'), + c('Legendary Spirit Thirst'), c('Legendary Magic Resistance'), + ]}, + { group: 'Utility', items: [ + c('Legendary Summoning Prowess'), c('Legendary Healing Prowess'), + c('Legendary Leadership'), c('Legendary Deception Prowess'), + c('Legendary Person Attunement'), c('Legendary Magic Item Tinkering Expertise'), + ]}, + { group: 'Defense', items: [ + c('Legendary Invulnerability'), c('Legendary Impenetrability'), + c('Legendary Impregnability'), c('Legendary Armor'), + ]}, + { group: 'Wards & Banes', items: [ + c('Legendary Flame Ward'), c('Legendary Frost Ward'), c('Legendary Acid Ward'), + c('Legendary Storm Ward'), c('Legendary Slashing Ward'), c('Legendary Piercing Ward'), + c('Legendary Bludgeoning Ward'), c('Legendary Piercing Bane'), c('Legendary Storm Bane'), + ]}, +]; + +export const JEWELRY_SLOTS = ['Ring', 'Bracelet', 'Neck', 'Trinket', 'Cloak']; +export const ARMOR_SLOTS = ['Head', 'Chest', 'Abdomen', 'Upper Arms', 'Lower Arms', + 'Hands', 'Upper Legs', 'Lower Legs', 'Feet', 'Shield']; + +export const WEAPON_TYPES: Array<{ value: string; label: string }> = [ + { value: '', label: 'All weapons' }, { value: 'heavy', label: 'Heavy' }, + { value: 'light', label: 'Light' }, { value: 'finesse', label: 'Finesse' }, + { value: 'two_handed', label: 'Two-handed' }, { value: 'bow', label: 'Bow' }, + { value: 'crossbow', label: 'Crossbow' }, { value: 'thrown', label: 'Thrown' }, + { value: 'caster', label: 'Wand/Staff/Orb' }, +]; + +export interface RatingDef { param: string; label: string; common?: boolean; } +export const RATING_DEFS: RatingDef[] = [ + { param: 'min_damage_rating', label: 'Damage rating', common: true }, + { param: 'min_crit_damage_rating', label: 'Crit damage', common: true }, + { param: 'min_heal_boost_rating', label: 'Heal boost', common: true }, + { param: 'min_vitality_rating', label: 'Vitality', common: true }, + { param: 'min_armor', label: 'Armor level' }, + { param: 'min_damage_resist_rating', label: 'Damage resist' }, + { param: 'min_crit_resist_rating', label: 'Crit resist' }, + { param: 'min_crit_damage_resist_rating', label: 'Crit dmg resist' }, + { param: 'min_healing_resist_rating', label: 'Healing resist' }, + { param: 'min_nether_resist_rating', label: 'Nether resist' }, + { param: 'min_healing_rating', label: 'Healing rating' }, + { param: 'min_dot_resist_rating', label: 'DoT resist' }, + { param: 'min_life_resist_rating', label: 'Life resist' }, + { param: 'min_sneak_attack_rating', label: 'Sneak attack' }, + { param: 'min_recklessness_rating', label: 'Recklessness' }, + { param: 'min_deception_rating', label: 'Deception' }, + { param: 'min_pk_damage_rating', label: 'PK damage' }, + { param: 'min_pk_damage_resist_rating', label: 'PK dmg resist' }, + { param: 'min_gear_pk_damage_rating', label: 'Gear PK dmg' }, + { param: 'min_gear_pk_damage_resist_rating', label: 'Gear PK resist' }, + { param: 'min_tinks', label: 'Tinks' }, +]; + +export interface ColumnDef { + key: string; label: string; sortKey?: string; defaultVisible: boolean; +} +// sortKey values must exist in the backend's sortMapping (search.go). +export const COLUMNS: ColumnDef[] = [ + { key: 'name', label: 'Item', sortKey: 'name', defaultVisible: true }, + { key: 'character_name', label: 'Character', sortKey: 'character_name', defaultVisible: true }, + { key: 'slot_name', label: 'Slot', defaultVisible: true }, + { key: 'value', label: 'Value', sortKey: 'value', defaultVisible: true }, + { key: 'wield_level', label: 'Wield', sortKey: 'level', defaultVisible: true }, + { key: 'spell_names', label: 'Spells / Cantrips', sortKey: 'spell_names', defaultVisible: true }, + { key: 'armor_level', label: 'Armor', sortKey: 'armor', defaultVisible: false }, + { key: 'max_damage', label: 'Max Dmg', sortKey: 'damage', defaultVisible: false }, + { key: 'workmanship', label: 'Work', sortKey: 'workmanship', defaultVisible: false }, + { key: 'item_set_name', label: 'Set', sortKey: 'item_set', defaultVisible: false }, + { key: 'object_class_name', label: 'Type', sortKey: 'item_type_name', defaultVisible: false }, + { key: 'burden', label: 'Burden', defaultVisible: false }, + { key: 'condition_percent', label: 'Cond %', defaultVisible: false }, + { key: 'last_updated', label: 'Updated', sortKey: 'last_updated', defaultVisible: false }, +]; + +export const PAGE_SIZE = 200; +export const COLUMNS_LS_KEY = 'inv.visibleColumns'; diff --git a/frontend/src/components/inventory/types.ts b/frontend/src/components/inventory/types.ts new file mode 100644 index 00000000..b72f08db --- /dev/null +++ b/frontend/src/components/inventory/types.ts @@ -0,0 +1,67 @@ +// Item shape returned by /api/inv/search/items (subset we render). +export interface InvItem { + name: string; + character_name: string; + slot_name: string | null; + value: number | null; + burden: number | null; + wield_level: number | null; + workmanship: number | null; + spell_names?: string[]; + is_equipped: boolean; + is_bonded: boolean; + is_attuned: boolean; + is_rare: boolean; + item_set_name?: string; + armor_level: number | null; + max_damage: number | null; + condition_percent: number | null; + object_class_name?: string; + material_name?: string; + last_updated?: string; +} + +export interface SearchResponse { + items: InvItem[]; + total_count: number; + page: number; + limit: number; + has_next: boolean; + has_previous: boolean; + error?: string; +} + +export type ItemType = 'all' | 'armor' | 'jewelry' | 'weapon' | 'clothing' | 'shirt' | 'pants'; + +export interface SearchFilters { + text: string; + /** 'all' → include_all_characters=true; otherwise explicit list. */ + characters: 'all' | string[]; + itemType: ItemType; + weaponType: string; // '' = all weapon types + slots: string[]; + cantrips: string[]; // full "Legendary X" value strings + spellContains: string; + /** min-rating param name -> value ('' = unset). */ + ratings: Record; + itemSet: string; // set id, '' = none + equippedOnly: boolean; + bonded: boolean; + attuned: boolean; + rare: boolean; + maxLevel: number | ''; + minValue: number | ''; + minWorkmanship: number | ''; + maxBurden: number | ''; + sortBy: string; + sortDir: 'asc' | 'desc'; + page: number; +} + +export const DEFAULT_FILTERS: SearchFilters = { + text: '', characters: 'all', itemType: 'all', weaponType: '', slots: [], + cantrips: [], spellContains: '', ratings: {}, itemSet: '', + equippedOnly: false, bonded: false, attuned: false, rare: false, + maxLevel: '', minValue: '', minWorkmanship: '', maxBurden: '', + sortBy: 'name', sortDir: 'asc', page: 1, +};