feat(frontend): inventory search types + filter/column constants

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-07-15 06:09:22 +02:00
parent 93982ac3dd
commit 5ddcecaece
2 changed files with 168 additions and 0 deletions

View file

@ -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';

View file

@ -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<string, number | ''>;
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,
};