fix(admin): theme, American English, and inventory/reservation tooling (#27)
Seven reported items, of which the first four had two root causes. The active tab was invisible in dark mode because colorPrimary was hardcoded to #1a1a1a in both themes. The accent now inverts with the theme, and colorTextLightSolid inverts with it, or a near-white accent would get antd's default white label and disappear. The Category tab, Tag tab, and item-form category selector ignored the theme entirely. antd declares main: lib/index.js and module: es/index.js, so importing from 'antd' resolves to the ES build while 'antd/lib/...' loads the CommonJS one — two copies, two React contexts, and no ConfigProvider for anything deep-imported. Switching those files to antd/es/* keeps the deep-import convention and shares the instance. This was introduced by my own use of the lib path; es is correct under Vite. Two storefront components had the same latent bug. "Colour" is now "Color". The Customers tab shows how many items each customer is holding, as a link opening the item list with a Release button. Release mirrors the customer's own cart removal — drop the cart row, return the item to available, guarded on 'reserved' so it can never resurrect a sold item — and deliberately sends no email about an action the customer did not take. The count is a subquery rather than another join, which would have multiplied rows and inflated order_count and total_spent_cents. The Inventory tab filters by category, tags, price, and status, reusing the storefront's parser and query builder so the two cannot drift. Reserved is one option in a Status filter rather than a standalone toggle. Also fixes two defects the screenshots exposed: the reserved-count link bubbled to the row handler and opened the customer drawer behind the dialog, and .admin-category-node had no CSS at all, so the tree node name, item count, and actions ran together as one string. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,8 @@ import Settings from './Settings';
|
||||
import Categories from './Categories';
|
||||
import Tags from './Tags';
|
||||
import CategoryTreeSelect from './CategoryTreeSelect';
|
||||
import InventoryFilters from './InventoryFilters';
|
||||
import { ItemFilters, EMPTY_FILTERS } from '../filters';
|
||||
|
||||
const { Header, Content } = Layout;
|
||||
const { Title } = Typography;
|
||||
@@ -34,9 +36,10 @@ function Inventory() {
|
||||
const [categories, setCategories] = useState<Category[]>([]);
|
||||
const [tags, setTags] = useState<TagRecord[]>([]);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [filters, setFilters] = useState<ItemFilters>(EMPTY_FILTERS);
|
||||
const { mode } = useThemeMode();
|
||||
|
||||
const load = () => fetchAdminItems().then(setItems);
|
||||
const load = (active: ItemFilters = filters) => fetchAdminItems(active).then(setItems);
|
||||
|
||||
// The item form needs the current category tree and tag list; both change
|
||||
// from the sibling tabs, so they're refetched whenever the modal opens.
|
||||
@@ -45,7 +48,13 @@ function Inventory() {
|
||||
fetchAdminTags().then(setTags)
|
||||
]);
|
||||
|
||||
useEffect(() => { load(); loadOptions(); }, []);
|
||||
// Refetch whenever the filters change — filtering is server-side so the
|
||||
// result stays correct regardless of how many items exist.
|
||||
useEffect(() => { load(filters); }, [filters]);
|
||||
useEffect(() => { loadOptions(); }, []);
|
||||
|
||||
function applyFilters(next: ItemFilters) { setFilters(next); }
|
||||
function clearFilters() { setFilters(EMPTY_FILTERS); }
|
||||
|
||||
function openNew() {
|
||||
setEditingItem(null);
|
||||
@@ -193,6 +202,15 @@ function Inventory() {
|
||||
<Title level={4} style={{ margin: 0 }}>Inventory</Title>
|
||||
<Button type="primary" onClick={openNew}>Add Item</Button>
|
||||
</div>
|
||||
|
||||
<InventoryFilters
|
||||
categories={categories}
|
||||
tags={tags}
|
||||
filters={filters}
|
||||
onChange={applyFilters}
|
||||
onClear={clearFilters}
|
||||
/>
|
||||
|
||||
<Table rowKey="id" dataSource={items} columns={columns} scroll={{ x: true }} />
|
||||
|
||||
<Modal title={editingItem ? 'Edit Item' : 'Add Item'} open={modalOpen} onOk={handleOk} confirmLoading={saving} onCancel={() => setModalOpen(false)} destroyOnHidden width={720}>
|
||||
|
||||
Reference in New Issue
Block a user