diff --git a/frontend/src/components/inventory/ActiveChips.tsx b/frontend/src/components/inventory/ActiveChips.tsx index 97c1e21e..95e324a0 100644 --- a/frontend/src/components/inventory/ActiveChips.tsx +++ b/frontend/src/components/inventory/ActiveChips.tsx @@ -1,5 +1,5 @@ import type { InventorySearch } from './useInventorySearch'; -import { RATING_DEFS } from './constants'; +import { RATING_DEFS, WEAPON_TYPES } from './constants'; interface Chip { label: string; gold?: boolean; remove: () => void; } @@ -12,9 +12,13 @@ export function ActiveChips({ search }: { search: InventorySearch }) { remove: () => update({ characters: 'all' }), }); if (f.itemType !== 'all') chips.push({ - label: f.itemType[0].toUpperCase() + f.itemType.slice(1) + (f.weaponType ? `: ${f.weaponType}` : ''), - remove: () => update({ itemType: 'all', weaponType: '' }), + label: f.itemType[0].toUpperCase() + f.itemType.slice(1), + remove: () => update({ itemType: 'all', weaponTypes: [] }), }); + for (const wt of f.weaponTypes) { + const label = WEAPON_TYPES.find(w => w.value === wt)?.label ?? wt; + chips.push({ label, remove: () => update({ weaponTypes: f.weaponTypes.filter(x => x !== wt) }) }); + } for (const s of f.slots) chips.push({ label: `Slot: ${s}`, remove: () => update({ slots: f.slots.filter(x => x !== s) }) }); for (const c of f.cantrips) chips.push({ label: c.replace(/^Legendary /, 'Leg. '), gold: true, diff --git a/frontend/src/components/inventory/FilterSidebar.tsx b/frontend/src/components/inventory/FilterSidebar.tsx index 8fb8d5d6..87ddebec 100644 --- a/frontend/src/components/inventory/FilterSidebar.tsx +++ b/frontend/src/components/inventory/FilterSidebar.tsx @@ -66,14 +66,22 @@ export function FilterSidebar({ search }: { search: InventorySearch }) { {types.map(([v, label]) => ( ))} {f.itemType === 'weapon' && ( - +
+ {WEAPON_TYPES.map(w => ( + + ))} +
)} diff --git a/frontend/src/components/inventory/ResultsTable.tsx b/frontend/src/components/inventory/ResultsTable.tsx index 3afdc724..1291009c 100644 --- a/frontend/src/components/inventory/ResultsTable.tsx +++ b/frontend/src/components/inventory/ResultsTable.tsx @@ -42,7 +42,11 @@ export function ResultsTable({ search, selected, onSelect }: { const [visible, setVisible] = useState>(loadVisible); const [pickerOpen, setPickerOpen] = useState(false); const scrollRef = useRef(null); - const cols = useMemo(() => COLUMNS.filter(c => visible.has(c.key)), [visible]); + const cols = useMemo(() => { + const eff = new Set(visible); + if (f.itemType === 'weapon') eff.add('od_rating'); + return COLUMNS.filter(c => eff.has(c.key)); + }, [visible, f.itemType]); const items = result?.items ?? []; useEffect(() => { diff --git a/frontend/src/components/inventory/constants.ts b/frontend/src/components/inventory/constants.ts index 92251f75..90748788 100644 --- a/frontend/src/components/inventory/constants.ts +++ b/frontend/src/components/inventory/constants.ts @@ -44,11 +44,13 @@ export const ARMOR_SLOTS = ['Head', 'Chest', 'Abdomen', 'Upper Arms', 'Lower Arm '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' }, + { value: 'heavy', label: 'Heavy' }, + { value: 'light', label: 'Light' }, + { value: 'finesse', label: 'Finesse' }, + { value: 'two_handed', label: 'Two-Handed' }, + { value: 'missile', label: 'Missile' }, + { value: 'war', label: 'War Magic' }, + { value: 'void', label: 'Void Magic' }, ]; export interface RatingDef { param: string; label: string; common?: boolean; } diff --git a/frontend/src/components/inventory/types.ts b/frontend/src/components/inventory/types.ts index 491bcefe..110d7264 100644 --- a/frontend/src/components/inventory/types.ts +++ b/frontend/src/components/inventory/types.ts @@ -39,7 +39,7 @@ export interface SearchFilters { /** 'all' → include_all_characters=true; otherwise explicit list. */ characters: 'all' | string[]; itemType: ItemType; - weaponType: string; // '' = all weapon types + weaponTypes: string[]; // [] = all weapon types slots: string[]; cantrips: string[]; // full "Legendary X" value strings spellContains: string; @@ -61,7 +61,7 @@ export interface SearchFilters { } export const DEFAULT_FILTERS: SearchFilters = { - text: '', characters: 'all', itemType: 'all', weaponType: '', slots: [], + text: '', characters: 'all', itemType: 'all', weaponTypes: [], slots: [], cantrips: [], spellContains: '', ratings: {}, itemSet: '', equipStatus: '', bonded: false, attuned: false, rare: false, maxLevel: '', minValue: '', minWorkmanship: '', maxBurden: '', diff --git a/frontend/src/components/inventory/useInventorySearch.ts b/frontend/src/components/inventory/useInventorySearch.ts index 443674f5..a74117d2 100644 --- a/frontend/src/components/inventory/useInventorySearch.ts +++ b/frontend/src/components/inventory/useInventorySearch.ts @@ -34,6 +34,7 @@ function validateFilterValue(key: keyof SearchFilters, value: unknown): unknown return undefined; case 'slots': case 'cantrips': + case 'weaponTypes': return isStringArray(value) ? value : undefined; case 'itemType': return typeof value === 'string' && ITEM_TYPES.has(value) ? value : undefined; @@ -48,7 +49,6 @@ function validateFilterValue(key: keyof SearchFilters, value: unknown): unknown return out; } case 'text': - case 'weaponType': case 'spellContains': case 'itemSet': case 'sortBy': @@ -104,7 +104,7 @@ export function buildParams(f: SearchFilters): URLSearchParams { case 'pants': p.set('pants_only', 'true'); break; case 'weapon': p.set('weapon_only', 'true'); - if (f.weaponType) p.set('weapon_type', f.weaponType); + if (f.weaponTypes.length) p.set('weapon_types', f.weaponTypes.join(',')); break; } if (f.slots.length) p.set('slot_names', f.slots.join(',')); diff --git a/frontend/src/styles/inventory.css b/frontend/src/styles/inventory.css index 3dffbc70..ddc801ce 100644 --- a/frontend/src/styles/inventory.css +++ b/frontend/src/styles/inventory.css @@ -49,6 +49,7 @@ .inv-fitem { display: flex; align-items: center; gap: 6px; padding: 2px 0; font-size: 12px; color: #bbb; cursor: pointer; } .inv-fitem:hover { color: #fff; } .inv-fitem input { accent-color: #88f; } +.inv-subgroup { margin: 2px 0 2px 10px; } .inv-subhead { color: #666; font-size: 9px; text-transform: uppercase; letter-spacing: 1px; margin: 6px 0 2px; } .inv-linky { color: #88f; font-size: 10px; cursor: pointer; } .inv-linky:hover { text-decoration: underline; }