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; }