From 3f5ba532c6a9d5936f1597e8039df9db40ed26e4 Mon Sep 17 00:00:00 2001 From: MayaTheShy Date: Sat, 21 Mar 2026 16:49:38 -0400 Subject: [PATCH] Add InventoryGrid component for displaying and managing inventory items --- web/client/src/components/InventoryGrid.jsx | 155 ++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 web/client/src/components/InventoryGrid.jsx diff --git a/web/client/src/components/InventoryGrid.jsx b/web/client/src/components/InventoryGrid.jsx new file mode 100644 index 0000000..ee66c43 --- /dev/null +++ b/web/client/src/components/InventoryGrid.jsx @@ -0,0 +1,155 @@ +import React, { useState, useCallback } from 'react'; +import { useInventoryStore } from '../store/inventoryStore'; +import { formatItemName, getItemEmoji, formatCount } from '../utils/itemUtils'; +import ItemIcon from './ItemIcon'; +import './InventoryGrid.css'; + +function InventoryGrid() { + const inventory = useInventoryStore((state) => state.inventory); + const searchQuery = useInventoryStore((state) => state.searchQuery); + const setSearchQuery = useInventoryStore((state) => state.setSearchQuery); + const getFilteredItems = useInventoryStore((state) => state.getFilteredItems); + const orderItem = useInventoryStore((state) => state.orderItem); + + const [selectedItem, setSelectedItem] = useState(null); + const [orderAmount, setOrderAmount] = useState(1); + const [sortBy, setSortBy] = useState('count'); // 'count', 'name' + + const items = getFilteredItems(); + + const sortedItems = [...items].sort((a, b) => { + if (sortBy === 'count') return (b.count || 0) - (a.count || 0); + if (sortBy === 'name') return (a.displayName || a.name || '').localeCompare(b.displayName || b.name || ''); + return 0; + }); + + const handleOrder = useCallback(async () => { + if (!selectedItem || orderAmount <= 0) return; + await orderItem(selectedItem.name, orderAmount); + }, [selectedItem, orderAmount, orderItem]); + + const handleItemClick = (item) => { + setSelectedItem(item); + setOrderAmount(1); + }; + + return ( +
+ {/* Search & controls bar */} +
+
+ ๐Ÿ” + setSearchQuery(e.target.value)} + className="search-input" + /> + {searchQuery && ( + + )} +
+
+ + +
+
+ {sortedItems.length} types ยท {(inventory.grandTotal || 0).toLocaleString()} items +
+
+ +
+ {/* Item grid */} +
+
+ {sortedItems.map((item) => ( +
handleItemClick(item)} + title={`${formatItemName(item.name)}\n${item.count} total`} + > + + {formatCount(item.count)} + {formatItemName(item.name)} +
+ ))} + {sortedItems.length === 0 && ( +
+ {searchQuery ? `No items matching "${searchQuery}"` : 'No items in storage'} +
+ )} +
+
+ + {/* Item detail / order panel */} + {selectedItem && ( +
+
+ +
+

{formatItemName(selectedItem.name)}

+ {selectedItem.name} +
+ +
+ +
+
+ Total + {(selectedItem.count || 0).toLocaleString()} +
+
+ Stacks + + {Math.ceil((selectedItem.count || 0) / 64)} + +
+
+ +
+

๐Ÿ“ค Order Items

+
+
+ + setOrderAmount(Math.max(1, parseInt(e.target.value) || 1))} + min="1" + max={selectedItem.count || 1} + /> + +
+
+ + + + + +
+ +
+
+
+ )} +
+
+ ); +} + +export default InventoryGrid;