Files
redefined-designs/frontend/src/main.tsx
T
bermudalambandClaude Opus 5 f537314259
SonarQube Analysis / sonarqube (pull_request) Successful in 4m23s
Tests / backend-unit (pull_request) Successful in 1m6s
Tests / backend-integration (pull_request) Failing after 4m55s
Tests / frontend-e2e (pull_request) Failing after 12m41s
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>
2026-08-17 15:49:05 -05:00

92 lines
3.4 KiB
TypeScript
Executable File

import React, { useEffect, useState } from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { ConfigProvider, theme as antdTheme } from 'antd';
import 'antd/dist/reset.css';
import App from './App';
import Admin from './admin/Admin';
import Login from './customer/Login';
import Register from './customer/Register';
import Account from './customer/Account';
import PrivacyPolicy from './customer/PrivacyPolicy';
import VerifyEmail from './customer/VerifyEmail';
import Cart from './cart/Cart';
import { CustomerAuthProvider } from './customer/CustomerAuthContext';
import { CartProvider } from './cart/CartContext';
import { ThemeModeProvider, useThemeMode } from './theme/ThemeContext';
import './styles.css';
// The brand accent is monochrome, so it inverts between themes rather than
// switching to a different hue.
const LIGHT_ACCENT = '#1a1a1a';
const DARK_ACCENT = '#f0f0f0';
const REDUCED_MOTION_QUERY = '(prefers-reduced-motion: reduce)';
// Respects the OS-level "reduce motion" accessibility setting by turning off
// antd's transitions. Beyond the accessibility win, animated popups are a
// standing source of flake in end-to-end tests, which drive the app with this
// preference enabled.
function usePrefersReducedMotion(): boolean {
const [prefers, setPrefers] = useState(
() => typeof window !== 'undefined' && window.matchMedia(REDUCED_MOTION_QUERY).matches
);
useEffect(() => {
const query = window.matchMedia(REDUCED_MOTION_QUERY);
const update = () => setPrefers(query.matches);
query.addEventListener('change', update);
return () => query.removeEventListener('change', update);
}, []);
return prefers;
}
function Root() {
const { mode } = useThemeMode();
const prefersReducedMotion = usePrefersReducedMotion();
return (
<ConfigProvider
theme={{
algorithm: mode === 'dark' ? antdTheme.darkAlgorithm : antdTheme.defaultAlgorithm,
token: {
// The accent inverts with the theme rather than staying near-black.
// Held constant, it rendered the active tab label in #1a1a1a on a
// dark background — invisible.
colorPrimary: mode === 'dark' ? DARK_ACCENT : LIGHT_ACCENT,
// Text drawn *on* the accent (primary buttons, selected rows) has to
// invert with it too, or a near-white accent gets antd's default
// white label and disappears.
colorTextLightSolid: mode === 'dark' ? LIGHT_ACCENT : '#ffffff',
motion: !prefersReducedMotion
}
}}
>
<BrowserRouter>
<Routes>
<Route path="/" element={<App />} />
<Route path="/admin" element={<Admin />} />
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} />
<Route path="/account" element={<Account />} />
<Route path="/cart" element={<Cart />} />
<Route path="/privacy" element={<PrivacyPolicy />} />
<Route path="/verify-email" element={<VerifyEmail />} />
</Routes>
</BrowserRouter>
</ConfigProvider>
);
}
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<ThemeModeProvider>
<CustomerAuthProvider>
<CartProvider>
<Root />
</CartProvider>
</CustomerAuthProvider>
</ThemeModeProvider>
</React.StrictMode>
);